Ускорение / замедление к движению цели и поражению - PullRequest
2 голосов
/ 15 апреля 2019

Я пытаюсь создать плавное движение камеры в 2D.Цель, которую я хочу поразить, потенциально может перемещаться на большое расстояние в одном кадре и не похожа на персонажа, плавно переходящего от А к В.

Я знаю о возможных решениях, таких как Vector2.Lerp(), нотакой подход только медленно замедляется, но резко ускоряется.

_position = Vector2.Lerp(_position, target, 0.5f * Time.deltaTime);

Я пытался реализовать поведение рулевого управления "прибыть", но не смог заставить его работать вместе с ускорением - особенно когда цель близка к текущей позиции.

Мне удалосьчтобы заставить его работать довольно хорошо на одной оси, но этот подход не работал при повторении на второй оси.

var decelerateRadius = GetDistanceFromAcceleration(acceleration, Mathf.Abs(_velocity));
var direction = target - _position;
var distance = Mathf.Abs(direction);
var a = acceleration * Time.deltaTime;

if (distance > 0.0005f)
{
    if (distance < decelerateRadius.x)
    {
        _velocity *= distance / decelerateRadius.x;
    }
    else
    {
        _velocity += direction.x >= 0.0f ? a : -a;
    }
}
else
{
    _velocity = 0.0f;
}

// move tracker
_position += _velocity * Time.deltaTime;

И мой метод расчета расстояния на основе ускорения:

private Vector2 GetDistanceFromAcceleration(float a, float vx, float vy)
{
    // derived from: a = (vf^2 - vi^2) / (2 * d)
    return new Vector2(vx * vx / (2.0f * a), vy * vy / (2.0f * a));
}

Моей последней попыткой было получение скользящего среднего значения цели, но она столкнулась с той же проблемой, что и лепрпинг.

Чтобы подвести итог требований:

  • Должен ускоряться
  • Должен замедляться и останавливаться у цели
  • Не должен "вращаться вокруг орбиты" или иным образом вращаться вокруг цели перед остановкой
  • Цель должна быть в состоянии двигаться
  • Может бытьограничено максимальной скоростью

Какие-либо советы, указатели или решения о том, как этого добиться?


Я также задавал вопрос в игре dev https://gamedev.stackexchange.com/questions/170056/accelerate-decelerate-towards-moving-target-and-hitting-it

1 Ответ

3 голосов
/ 15 апреля 2019

Проблема с вашим lerp также в том, что вы фактически никогда не достигаете целевой позиции, вы просто становитесь очень и очень близкими и маленькими.

Я думал о чем-то вроде этого

  • так долгопоскольку вы уже находитесь в позиции target s, не двигайтесь.Включите режим орбиты
  • , находясь за пределами определенной targetRadius вокруг позиции target, и увеличивайте до maxVelocity
  • , если скорость в пределах определенной targetRadius вокруг позиции target замедляетсяв зависимости от distance / radius будет значение от 1 до 0

Чтобы получить расстояние, уже есть Vector2.Distance, которое вы могли бы / должны использовать.

Для движения я бы порекомендовал Vector2.MoveTowards, что также позволяет избежать превышения цели.

что-то вроде

public class SmoothFollow2D : MonoBehaviour
{
    [Header("Components")]
    [Tooltip("The target this will follow")]
    [SerializeField] private Transform target;

    [Header("Settings")]
    [Tooltip("This may never be 0!")]
    [SerializeField] private float minVelocity = 0.1f;
    [SerializeField] private float maxVelocity = 5.0f;

    [Tooltip("The deceleration radius around the target.\nThis may never be 0!")]
    [SerializeField] private float targetRadius = 1.0f;

    [Tooltip("How much speed shall be added per second?\n" +
             "If this is equal to MaxVelocity you know that it will take 1 second to accelerate from 0 to MaxVelocity.\n" +
             "Should not be 0")]
    [SerializeField] private float accelerationFactor = 3.0f;


    private float _currentVelocity;
    private float _lastVelocityOutsideTargetRadius;

    private bool _enableOrbit;
    public bool EnableOrbit
    {
        get { return _enableOrbit; }
        private set
        {
            // if already the same value do nothing
            if (_enableOrbit == value) return;

            _enableOrbit = value;

            // Whatever shall be done if orbit mode is enabled or disabled
        }
    }

    private void Update()
    {
        if (target == null) return;

        var distanceToTarget = Vector2.Distance(transform.position, target.position);

        // This is the threshold Unity uses for equality of vectors (==)
        // you might want to change it to a bigger value in order to
        // make the Camera more stable e.g.
        if (distanceToTarget <= 0.00001f)
        {
            EnableOrbit = true;

            // do nothing else 
            return;
        }

        EnableOrbit = false;

        if (distanceToTarget <= targetRadius)
        {
            // decelerate
            // This will make it slower 
            // the closer we get to the target position
            _currentVelocity = _lastVelocityOutsideTargetRadius * (distanceToTarget / targetRadius);

            // as long as it is not in the final position
            // it should always keep a minimum speed
            _currentVelocity = Mathf.Max(_currentVelocity, minVelocity);
        }
        else
        {
            // accelerate
            _currentVelocity += accelerationFactor * Time.deltaTime;

            // Limit to MaxVelocity
            _currentVelocity = Mathf.Min(_currentVelocity, maxVelocity);

            _lastVelocityOutsideTargetRadius = _currentVelocity;
        }

        transform.position = Vector2.MoveTowards(transform.position, target.position, _currentVelocity * Time.deltaTime);
    }


    // Just for visualizing the decelerate radius around the target
    private void OnDrawGizmos()
    {
        if (target) Gizmos.DrawWireSphere(target.position, targetRadius);
    }
}

MinVelocity действительно необходимо длякрайний случай, когда цель перемещается не дальше, чем TargetRadius и lastVelocityOutsideTargetRadius si 0.В этом случае ускорение не происходит, поэтому lastVelocityOutsideTargetRadius никогда не обновляется.

Со значениями вам нужно немного поиграть;)


Возможно, это еще не идеально, но яНадеюсь, что это хорошая отправная точка для дальнейшего развития (выглядит медленным из-за 15 кадров в секунду для Gif;))

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...