我已经尝试寻找这个问题的解决方案有一段时间了,但我觉得我缺乏对物理原理如何找到正确解决方案的理解。希望这里有人可以帮助我!
问题
如标题所述 - 我想要完成的是将移动物体减速到完全停止并到达特定距离。
上下文
我特别尝试在玩家 Controller 中实现此功能,一旦不再提供输入,我就会获取移动对象的当前速度,并将其从释放点的位置减慢到停止 x 个单位。
当前方法
目前,我知道我知道 (a) 初始速度 (b) 目标速度 (c) 在速度变化过程中行驶的距离以及 (d) 要到达的目标距离。
我已经能够使用此脚本以 5 单位/秒的特定速度使其工作:
public class DecelToStop : MonoBehaviour
{ Rigidbody2D rb;
public float speed = 5f; // velocity of object while input is pressed
public float stoppingDistance = 3f; // distance object should stop at on input released
public float stopTimeMultiplier = 3.5f; // multiplier applied to time step to reach desired stopping distance
bool inputIsReleased = false;
float decelerationNeeded = 0;
public float GetDeceleration(float initalVelocity, float targetVelocity)
{
float travelTime = stoppingDistance / initalVelocity; // total time needed to reach a stop
float velocityChange = targetVelocity - initalVelocity;// total change in velocity
float decelTimeMultiplier = Mathf.Sqrt(stoppingDistance * stopTimeMultiplier); // how much to multiply the travel time by
float deceleration = initalVelocity / (travelTime * decelTimeMultiplier); //amount of deceleration to apply each fixed update
return deceleration;
}
private void FixedUpdate()
{
// get deceleration needed on input release
if (!inputIsReleased)
{
decelerationNeeded = GetDeceleration(speed, 0);
inputIsReleased = true;
}
// apply total force needed by applying the inital speed and the deceleration neeed
if (rb.velocity.x != 0)
{
rb.AddForce(new Vector2(speed, 0));
rb.AddForce(new Vector2(decelerationNeeded, 0));
}
}
}
我当前方法的问题是,一旦我更改了速度变量,stopTimeMultipler 就变成了我想要避免的情况 - 这是一堆猜测工作,以找到一切正常工作所需的确切值。
我确信这种方法存在多个缺陷 - 就像我说的我对物理计算没有很好的理解 - 所以如果你有一个解决方案,如果你可以像与 5 交谈一样解释它一岁那就太好了!该解决方案不需要达到精确的停止距离 - 只要它相对接近(0.2 单位以内)并且可以随着不同的速度和停止距离进行缩放,就可以存在一些变化。
最佳答案
好的,在花了更多时间之后,我已经找到了一个可扩展的解决方案。
我已经完全修改了我的方法 - 而是使用这两个视频中所示的方法切换到加速和减速我的刚体: https://www.youtube.com/watch?v=uVKHllD-JZk https://www.youtube.com/watch?v=YskC8h3xFVQ&ab_channel=BoardToBitsGames (我建议观看这些内容以充分了解加速是如何实现的)
这些视频使我能够在指定的时间内将物体加速和减速到目标速度。
从这里我可以编写一个方法,将提供的距离值转换为找到应用每次更新的正确加速度所需的时间变量。
我在这里找到了执行此操作的公式:https://physics.stackexchange.com/questions/18974/what-do-i-need-to-do-to-find-the-stopping-time-of-a-decelerating-car
但对于 C# 实现,请查看下面代码中的 ConvertDistanceToVelocityStep() 方法。
到目前为止,我的所有测试都表明,一旦不再提供输入,这种方法只允许提供最大速度和所需的停止距离,以将移动物体减慢至在指定距离处完全停止。
这是带有注释的完整脚本 - 如果您有任何优化或建议的改进,请随时将它们留在下面。
public class Accelerator : MonoBehaviour
{ Rigidbody2D m_Body2D;
// - Speed
public float maxSpeed = 6f;
// - Distance
public float stoppingDistance = 1f;
public float accelDistance = 1f;
// - Time
float timeZeroToMax = 2.5f;
float timeMaxToZero = 6f;
float accelRatePerSec;
float decelRatePerSec;
float xVel;
public bool inputPressed = false;
public bool allowInputs = true;
Vector2 lastHeldDirection;
// - get any needed references
private void Awake()
{
m_Body2D = GetComponent<Rigidbody2D>();
}
// - convert distance values into an acceleration to apply each update
void ConvertDistanceToVelocityStep()
{
//acceleration
timeZeroToMax = (2 * accelDistance) / (maxSpeed - 0);
accelRatePerSec = maxSpeed / timeZeroToMax;
//deceleration
timeMaxToZero = (2 * stoppingDistance) / (0 + maxSpeed);
decelRatePerSec = -maxSpeed / timeMaxToZero;
}
private void Start()
{
ConvertDistanceToVelocityStep();
xVel = 0;
}
private void Update()
{
// if inputs are allowed - check when horizontal buttons are pressed
if (allowInputs)
{
if (Input.GetButtonDown("Horizontal"))
inputPressed = true;
else if (Input.GetButtonUp("Horizontal"))
inputPressed = false;
}
else inputPressed = false;
}
private void FixedUpdate()
{
// if a valid input is provided
if (inputPressed && allowInputs)
{
// get direction
InputDirection();
// get acceleration
Accelerate(accelRatePerSec);
// apply acceleration in desired direction
m_Body2D.velocity = new Vector2(lastHeldDirection.x * xVel, m_Body2D.velocity.y);
}
// if input no longer pressed
else
{
// while still moving
if (Mathf.Abs(m_Body2D.velocity.x) > 0.01f)
{
// get deceleration
Accelerate(decelRatePerSec);
// apply deceleration in last held direction
m_Body2D.velocity = new Vector2(lastHeldDirection.x * xVel, m_Body2D.velocity.y);
}
else
// bring x velocity to zero
m_Body2D.velocity = new Vector2(0, m_Body2D.velocity.y);
}
}
// calculate x velocity to move rigidbody
void Accelerate(float accelRate)
{
xVel += accelRate * Time.deltaTime;
xVel = Mathf.Clamp(xVel, 0, maxSpeed);
}
Vector2 InputDirection()
{
// get both axis of input
float hor = Input.GetAxis("Horizontal");
float vert = Input.GetAxis("Vertical");
// save to vector2
Vector2 inputDir = new Vector2(hor, vert);
// round last held direction to whole number
if (Mathf.Abs(inputDir.x) > 0.25f)
{
if (inputDir.x > 0)
lastHeldDirection.x = 1;
else lastHeldDirection.x = -1;
}
//normalize diagonal inputs
if (inputDir.magnitude > 1)
inputDir.Normalize();
// return input direction
return inputDir;
}
}
关于c# - UnityPhysics - 计算在指定距离处停止所需的减速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72332173/