c# - UnityPhysics - 计算在指定距离处停止所需的减速度

标签 c# unity-game-engine game-physics rigid-bodies

我已经尝试寻找这个问题的解决方案有一段时间了,但我觉得我缺乏对物理原理如何找到正确解决方案的理解。希望这里有人可以帮助我!

问题

如标题所述 - 我想要完成的是将移动物体减速到完全停止并到达特定距离。

上下文

我特别尝试在玩家 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/

相关文章:

android - 在Android游戏中动态计算圆的点数

c# - 从 C# 调用我自己的 Java 代码

c# - 将相机和音频监听器合并到 Unity 中的 OpenCV 视频中

c# - WPF路径碰撞检测?

unity-game-engine - 加载 Admob 智能横幅时游戏崩溃

ios - 升级到 Unity 4.6.1 后 Xcode 6.1.1 链接器错误 : entry point (_main) undefined. for architecture armv7

game-physics - 使用 JSON 和 Tiled 在 Phaser 中创建碰撞层

java - 在 Box 2D 中为一袋液体建模

c# - 使用 Filehelpers 处理 DelimitedRecord 中的换行符

c# - NServiceBus - NServiceBus.Host 作为发布者,WPF 应用程序作为订阅者。如何?