Как исправить ошибку в FixedUpdate?

Смысл кода достаточно простой. Удерживая пробел, я увеличиваю значение высоты прыжка на заранее рассчитанное значение (инкремент). Когда высота прыжка будет больше или равна максимально допустимой высоте прыжка - прыгаю. Значение инкремента подобрано так, что от начала удержания пробела, до прыжка должно пройти не более 1.5 секунд.

// Фиксированный вызов кода - у меня по умолчанию 0.02 (50 раз в секунду).
void FixedUpdate() 
{

// Прыжок с накоплением высоты прыжка, в зависимости от длительности удержания пробела.
    if (isJumping==true)
    {
    Debug.Log("Начало="+Time.time);
            
    currentJumpHeight+=jumpHeightIncrement;
    if (currentJumpHeight>=maxJumpHeight) jumpNow=true;   

    if (jumpNow==true) Debug.Log("Конец="+Time.time);
    }

}

  void Jump() 
    {
    Debug.Log("Начало метода прыжка="+Time.time);

    initialVelocity = Mathf.Sqrt(2 * currentJumpHeight * Physics2D.gravity.magnitude);
    rb.velocity = Vector2.up * initialVelocity; 

    Debug.Log("Завершение метода прыжка="+Time.time);
    }

В консоли вижу Начало=1,36 Конец=2,86 (То есть 1.5 - все нормально.)

Начало метода прыжка=2,860332 Завершение метода прыжка=2,860332

Судя по отладке задержек нет.

Но на сцене объект прыгает через 3 секунды, после удержания пробела. А должен через 1.5. В отладке - 1.5. В методе тоже 1.5. Откуда задержка?

Как исправить ошибку в FixedUpdate?

PS Я могу сделать прыжок как хочу, через 1.5 сек увеличив инкремент в 3 раза, то есть попросту быстрее вызвав метод Jump. Но это не нормально т.к. непонятно почему происходит такая задержка.


Ответы (1 шт):

Автор решения: Yaroslav
public class Foo : MonoBehaviour
{
    // поля нужно показывать, потому что те кто читают код, на них ориентируются

    // Increment в программирование это int +1/-1, по этому термин в названии лживый,
    // а поле должно называться что-то типа _heightGrowSpeed
    [SerializeField] private float jumpHeightIncrement = 2;
    [SerializeField] private float maxJumpHeight = 3;
    // тоже лживое, _isJumpPreparing
    private bool isJumping;
    // почему это поле вообще существует?
    private bool jumpNow;
    private float currentJumpHeight;
    private Rigidbody rb;
    // смысл выносить это значение в поле?
    private float initialVelocity;

    // FixedUpdate? с чего вдруг? тут нет обработки физики, это должно быть в обычном Update
    void FixedUpdate ()
    {
        // вот что бы в апдейтах не писать такие ifы на лимитированные по времени события существуют карутины,
        // и всякие мусорные флажки типа isJumping исчезают из полей класса
        if (isJumping == true)
        {
            // много же в логе "Начал" и каждый пишет свое
            Debug.Log("Начало=" + Time.time);

            currentJumpHeight += jumpHeightIncrement;
            // jumpNow... очередной мусорный флажек который... да ну хер его знает, тварь без рода и племени
            if (currentJumpHeight >= maxJumpHeight) jumpNow = true;

            if (jumpNow == true) Debug.Log("Конец=" + Time.time);
        }
    }

    // твой вопрос про "почему время дольше?", мой вопрос "почему вообще работает?" никто метод Jump не использует
    void Jump ()
    {
        Debug.Log("Начало метода прыжка=" + Time.time);

        // и почему velocity Y тела нельзя было тупо рисвоить это значение? для чего initialVelocity в поле класса?
        initialVelocity = Mathf.Sqrt(2 * currentJumpHeight * Physics2D.gravity.magnitude);
        rb.velocity = Vector2.up * initialVelocity;

        // ну и че? "Завершение" от "Начало" отличается? а почему вообще должно? 
        Debug.Log("Завершение метода прыжка=" + Time.time);
    }
}

Учись так писать:

[RequireComponent(typeof(Rigidbody2D))]
public class Jump2D : MonoBehaviour
{
    // программисты называют поля классов начиная с нижнего подчеркивания _!
    private Rigidbody2D _body;

    protected virtual void OnEnable () => _body = GetComponent<Rigidbody2D>();

    public virtual void DpJump (float height)
    {
        Vector3 velocity = _body.velocity;
        velocity.y = Mathf.Sqrt(2 * height * Physics2D.gravity.magnitude);
        _body.velocity = velocity;
    }
}
public class PreparedJump2D : Jump2D
{
    // не приходится в табуре переменных использовать "jump", поскольку весь класс только про это.
    // нет ни управления, ни чего лишнего, ТОЛЬКО механика прыжка,
    // которую можно прицепить и к персонажу и к врагу, да хоть к дверной ручке. 
    // Single Responsibility Principle!!!

    // высота не столько max, сколько верхняя планка(целевая), 
    // дойдя до которой подготовка окончена
    [SerializeField] private float _targetHeight = 3;
    [SerializeField] private float _heightGrowSpeed = 2;
    private IEnumerator _preparing;
    // что такое "текущая" (current), трудно понять, 
    // все таки "накопленная" в контексте prepare и  grow speed имеет смысл
    private float _accumulatedHeight;

    public bool IsPrepare => _preparing != null;
    public float TargetHeight => _targetHeight;

    public override void DpJump (float height)
    {
        base.DpJump(height);
        StopPrepare();
    }

    public void PrepareToJump ()
    {
        if (IsPrepare)
        {
            Debug.LogWarning("Can't prepare to jump. Jump ss already preparing");
            return;
        }
        _preparing = Preparing();
        StartCoroutine(_preparing);
    }

    public void JumpPrematurely ()
    {
        if (IsPrepare)
        {
            Debug.LogWarning("Can't jump prematurely. Jump not prepareing");
            return;
        }
        DpJump(_accumulatedHeight);
    }

    public void StopPrepare ()
    {
        if (IsPrepare == false)
            return;
        StopCoroutine(_preparing);
        _preparing = null;
    }

    // хорошие программисты всегда пишут методы доступа, включая private!
    private IEnumerator Preparing ()
    {
        _accumulatedHeight = 0;
        while (_accumulatedHeight < _targetHeight)
        {
            yield return null;
            _accumulatedHeight += _heightGrowSpeed * Time.deltaTime;
        }
        DpJump(_targetHeight);
    }
}

К нейменгу это не придирки, в этом весь смысл. Задача программиста это формализация, архитектура и понятный нейминг, без которого в этой архитектуре невозможно ориентироваться, а не написание кода. Даже при визуальном программирование, где кода нет, этим всем занимается программист.

→ Ссылка