我正在尝试在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
答案 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
是必需的,当目标移动的距离不超过TargetRadius
和lastVelocityOutsideTargetRadius
且仍为0
时。在这种情况下,不会加速,因此lastVelocityOutsideTargetRadius
永远不会更新。
使用这些值,您必须发挥一些作用;)
可能还不是很完美,但我希望这是一个进一步发展的良好起点(由于Gif的帧率为15 FPS,因此看起来太迟了))