Как сделать кнопку-пьедестал из игры 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());
    }
→ Ссылка