Как исправить ошибку в 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 шт):
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);
}
}
К нейменгу это не придирки, в этом весь смысл. Задача программиста это формализация, архитектура и понятный нейминг, без которого в этой архитектуре невозможно ориентироваться, а не написание кода. Даже при визуальном программирование, где кода нет, этим всем занимается программист.