朝目标移动并朝目标加速/减速

时间:2019-04-15 14:24:59

标签: c# unity3d

我正在尝试在2D模式下创建平滑的相机运动。我想击中的目标可能会在一帧中移动很长一段距离,而不像角色从A平稳移动到B。

我知道使用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));
}

我的最后一次尝试是对目标进行滚动平均,但是它遇到了与忍受相同的问题。

总结要求:

  • 必须加速
  • 必须减速并停在目标位置
  • 在停止之前,不得“绕轨道”或以其他方式围绕目标摆动
  • 目标必须能够移动
  • 可能受到最大速度的限制

有关如何实现此目标的任何提示,指示和解决方案?


我也已经在游戏开发人员那里问了这个问题 https://gamedev.stackexchange.com/questions/170056/accelerate-decelerate-towards-moving-target-and-hitting-it

1 个答案:

答案 0 :(得分:3)

束带的问题还在于,您实际上从未到达目标位置,只是变得非常非常小。

我想到了这样的事情

  • 只要您已经在target位置,就不要移动。启用轨道模式
  • 不在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是必需的,当目标移动的距离不超过TargetRadiuslastVelocityOutsideTargetRadius且仍为0时。在这种情况下,不会加速,因此lastVelocityOutsideTargetRadius永远不会更新。

使用这些值,您必须发挥一些作用;)


可能还不是很完美,但我希望这是一个进一步发展的良好起点(由于Gif的帧率为15 FPS,因此看起来太迟了))

enter image description here