Как сделать кнопку-пьедестал из игры Portal на движке Unity2D
Я бы хотел добавить для своей игры кнопку-пьедестал (как в игре Portal). Работать она должна следующим образом - когда игрок находится рядом с кнопкой, он должен нажать на клавишу E на или на кнопку на экране, если игрок играет с телефона. После этого активируется объект, привязанный к кнопке (например цепь, которая должна поднять платформу наверх). Я пробовал сделать это самостоятельно, но ничего не получается. Вот как я пытался реализовать это:
public class Player : MonoBehaviour // скрипт игрока в сокращении (показано только то, что связано с моей проблемой)
{
[Header("Main")]
ButtonPedestal buttonPedestal; // сама кнопка-пьедестал
private bool buttonPedestalPressed; // проверка на то, нажата ли кнопка E
void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
buttonPedestal = FindObjectOfType<ButtonPedestal>(); // находим кнопку на сцене
}
public void OnButtonPedestalPressed() // функция, которая активируется при нажатии кнопки E или кнопки на экране.
{
buttonPedestalPressed = !buttonPedestalPressed;
Debug.Log("Кнопка E нажата");
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
OnButtonPedestalPressed();
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("ButtonPedestal") && buttonPedestalPressed)
{
buttonPedestal.ActivateLinkedObject(); // вызываем функцию в скрипте кнопки пьедестала
Debug.Log("Активация началась");
}
}
}
Скрипт кнопки:
public class ButtonPedestal : MonoBehaviour
{
public ButtonObject buttonObject; // объект, который должна активировать кнопка
public void ActivateLinkedObject()
{
if (buttonObject.objectType == ButtonObject.ObjectType.Chain)
{
buttonObject.ChainUp();
Debug.Log("Сейчас цепь поднимется");
}
}
}
Скрипт для объекта, привязанного к кнопке.
public class ButtonObject : MonoBehaviour
{
public ObjectType objectType;
public enum ObjectType {Chain, CubeDispenser}; // Тип объекта (этих типов может быть сколько угодно)
public float speed; // скорость, с которой цепь поднимает платформу
public Transform pointA; // точка, у которой цепь находится, когда она не активирована
public Transform pointB; // точка, к которой цепь вместе с платформой должна двигаться.
void Start()
{
transform.position = pointA.position
}
public void ChainUp()
{
transform.position = Vector2.MoveTowards(pointA.position, pointB.position, Time.deltaTime * speed);
Debug.Log("Цепь поднялась");
}
}
Ответы (1 шт):
Я не понял, что у вас не получается, поэтому сделал все по-своему:)
Итак, есть абстрактный класс любого объекта, с которым можно взаимодействовать (ведь в игре могут быть объекты помимо цепи, которые можно активировать по кнопке):
public abstract class InteractableObject : MonoBehaviour
{
public abstract void OnInteraction();
}
Класс подвижной цепи:
public class MovableChain : InteractableObject
{
[SerializeField] private Vector2 TargetPos;
[SerializeField] private float Speed;
public override void OnInteraction()
{
StartCoroutine(MoveChain());
}
public IEnumerator MoveChain ()
{
var Delay = new WaitForEndOfFrame();
while ((Vector2) transform.position != TargetPos)
{
transform.position = Vector3.MoveTowards(transform.position, TargetPos, Speed);
yield return Delay;
}
}
}
- Вообще цепи обычно двигаются строго вертикально, поэтому можно хранить не Vector3 TargetPos, а float TargetPosY.
Класс кнопки:
public class Button : MonoBehaviour
{
[SerializeField]
private InteractableObject ControlledObject;
private void OnTriggerEnter2D(Collider2D collision)
{
ControlledObject.OnInteraction();
}
}
УЛУЧШЕНИЯ:
1) К одной кнопке можно прицепить несколько объектов:
public class Button : MonoBehaviour
{
[SerializeField]
private InteractableObject[] ControlledObjects;
private void OnTriggerEnter2D(Collider2D collision)
{
foreach (var obj in ControlledObjects)
{
obj.OnInteraction();
}
}
}
2) Двигаемые объекты пинг-понг (туда-сюда):
public class MovableObject : InteractableObject
{
[SerializeField] private Vector2 TargetPos;
[SerializeField] private float Speed;
[SerializeField] private bool PingPong; // Двигается ли туда-сюда или только в 1 сторону и там останавливается
private Vector3 StartPos;
private void Start()
{
StartPos = transform.position;
}
public override void OnInteraction()
{
StartCoroutine(MoveChain());
}
public IEnumerator MoveChain ()
{
while (PingPong) // Если пинг-понг
{
yield return MoveToTarget(); // Двигаем в 1 сторону
(StartPos, TargetPos) = (TargetPos, StartPos); // Меняем местами старт и целевую позицию
}
}
public IEnumerator MoveToTarget ()
{
var Delay = new WaitForEndOfFrame();
while ((Vector2)transform.position != TargetPos)
{
transform.position = Vector3.MoveTowards(transform.position, TargetPos, Speed);
yield return Delay;
}
}
}
3) Событие на отпускание кнопки:
Класс кнопки:
public class Button : MonoBehaviour
{
[SerializeField] private InteractableObject[] ControlledObjects;
[SerializeField] private bool IsCancellable;
private void OnTriggerEnter2D(Collider2D collision) {...}
private void OnTriggerExit2D(Collider2D collision)
{
if (IsCancellable)
{
foreach (var obj in ControlledObjects)
{
obj.OnInteractionEnd();
}
}
}
}
Класс абстрактного объекта:
public abstract class InteractableObject : MonoBehaviour
{
public abstract void OnInteraction();
public abstract void OnInteractionEnd();
}
Класс двигаемого объекта:
public class MovableObject : InteractableObject
{
...
private Vector3 StartPos;
private Coroutine Action;
private void Start() {...}
public override void OnInteraction()
{
Action = StartCoroutine(MoveChain());
}
public override void OnInteractionEnd()
{
// Можно остановить движение:
StopCoroutine(Action);
// Можно обратить движение в другую сторону:
StopCoroutine(Action);
Action = StartCoroutine(MoveChain());
}