我正在 Unity 2D 中开发一个从上到下的汽车游戏,然而我尝试调整汽车的驾驶性能时遇到了一个巨大的困难。 我构建了一个自定义的双轮物理模型,该模型将力直接应用到前轮和后轮的位置上,使用 AddForceAtPosition

我卡在一个死循环中,我的调整设置之间互相冲突。 如果我增加侧阻尼值(frontSideDampbackSideDamp),车辆的方向感应很好,车辆在直线上行驶得很好,但完全无法漂移。 如果我降低设置使其能够漂移,车辆则失去了紧密的方向感,车辆在正常驾驶时像冰球在冰面上滑行一样。

如何在这样的模型中平衡紧密的方向感和漂移的好处呢? 我是否需要实现一个动态滑动公式,还是我的数学完全缺乏什么呢?

以下是我的当前控制脚本:

using Unity.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class MyPlayerController : MonoBehaviour
{
    // 全局组件
    private Rigidbody2D playerRB;

    // 速度变量
    [SerializeField, ReadOnly]
    public float maxSpeed;
    public float acceleration;
    public float brakingForce;
    public float decceleration;
    public float linearDamping;

    public float frontSideDamp;
    public float backSideDamp;

    public float currentSpeed = 0f;

    // 转向变量
    public float angularDamping;
    public float maxTurn;
    public float turnspeed; // 每秒角度
    private float currentTurn;

    // 全局输入变量
    private float horizontalInput;
    private float verticalInput;

    private bool isDrifting;

    private void Awake()
    {
        playerRB = GetComponent<Rigidbody2D>();
        playerRB.angularDamping = angularDamping;
        maxSpeed = acceleration / linearDamping;
    }

    void Update()
    {
        if (Keyboard.current.wKey.isPressed && !Keyboard.current.sKey.isPressed)
        {
            verticalInput = 1f;
        }
        else if (Keyboard.current.sKey.isPressed && !Keyboard.current.wKey.isPressed)
        {
            verticalInput = -1f;
        }
        else
        {
            verticalInput = 0f;
        }

        if (Keyboard.current.dKey.isPressed && !Keyboard.current.aKey.isPressed)
        {
            horizontalInput = -1f;
        }
        else if (Keyboard.current.aKey.isPressed && !Keyboard.current.dKey.isPressed)
        {
            horizontalInput = 1f;
        }
        else
        {
            horizontalInput = 0f;
        }

        // 1. 计算目标角度(玩家想要的方向)
        float targetTurn = horizontalInput * maxTurn;

        // 2. 平滑地将当前转向转移到目标转向。
        // 如果水平输入是 0,目标转向是 0,所以它自然会自动中心。
        currentTurn = Mathf.MoveTowards(currentTurn, targetTurn, turnspeed * Time.deltaTime);
    }

    void FixedUpdate()
    {
        currentSpeed = Vector2.Dot(playerRB.linearVelocity, transform.up);

        float force = 0f;
        if (verticalInput < 0f)
        {
            if (currentSpeed < 0f)
            {
                force = -decceleration;
            }
            else
            {
                force = -brakingForce;
            }
        }
        else if (verticalInput > 0f)
        {
            if (currentSpeed > 0f)
            {
                force = acceleration;
            }
            else
            {
                force = brakingForce;
            }
        }

        Vector2 backWheel = transform.position - transform.up * 1.75f;
        Vector2 frontWheel = transform.position + transform.up * 1.75f;

        // 给后轮施加力
        playerRB.AddForceAtPosition(transform.up * force, backWheel);
        playerRB.AddForceAtPosition(-Vector2.Dot(transform.up, playerRB.linearVelocity) * transform.up * linearDamping / 2, backWheel);
        playerRB.AddForceAtPosition(-Vector2.Dot(transform.right, playerRB.linearVelocity) * transform.right * backSideDamp / 2, backWheel);

        // 给前轮施加力
        Vector2 newForward = Quaternion.AngleAxis(currentTurn, Vector3.forward) * transform.up;
        Vector2 newRight = Quaternion.AngleAxis(currentTurn, Vector3.forward) * transform.right;
        playerRB.AddForceAtPosition(newForward * -Vector2.Dot(newForward, playerRB.linearVelocity) * linearDamping / 2, frontWheel);
        playerRB.AddForceAtPosition(newRight * -Vector2.Dot(newRight, playerRB.linearVelocity) * frontSideDamp / 2, frontWheel);

        // 打印水平(漂移)比率
        float lateralSpeed = Vector2.Dot(playerRB.linearVelocity, transform.right);
        float driftRatio = playerRB.linearVelocity.magnitude > 0.1f
            ? Mathf.Abs(lateralSpeed) / playerRB.linearVelocity.magnitude
            : 0f;

        print(driftRatio);
    }

    private void OnGUI()
    {
        // 这将在游戏窗口的左上角打印一个标签
        GUI.Label(new Rect(10, 10, 200, 20), "Speed: " + currentSpeed.ToString("F2"));
    }

    private void OnValidate()
    {
        if (playerRB != null)
        {
            playerRB.angularDamping = angularDamping;
        }
        maxSpeed = acceleration / linearDamping;
    }

    public float getDriftRatio()
    {
        float lateralSpeed = Vector2.Dot(playerRB.linearVelocity, transform.right);
        float driftRatio = playerRB.linearVelocity.magnitude > 0.1f
            ? Mathf.Abs(lateralSpeed) / playerRB.linearVelocity.magnitude
            : 0f;

        return driftRatio;
    }
}

这个脚本使用 Unity 2D 的 Rigidbody2D 组件来模拟汽车的物理行为。 它使用 AddForceAtPosition 方法来给前轮和后轮施加力,实现汽车的转向和加速。 脚本还包含一些调试代码,用于打印汽车的速度和漂移比率。

然而,脚本中的一个主要问题是,它没有正确地平衡汽车的转向和漂移。 转向时,汽车会变得过于灵敏,而漂移时,汽车会变得过于难以控制。 这是因为脚本中的调节设置之间互相冲突,导致汽车的行为变得不稳定。

要解决这个问题,需要重新设计脚本中的调节设置和物理模型。 可能的解决方案包括:

  1. 使用动态滑动公式:滑动公式可以帮助汽车在漂移时保持稳定性,而不至于变得过于难以控制。
  2. 优化汽车的物理模型:汽车的物理模型需要优化,以便在转向和漂移时能够平衡汽车的行为。
  3. 调整调节设置:调节设置需要调整,以便汽车在转向和漂移时能够保持稳定性。

总之,解决这个问题需要重新设计脚本中的调节设置和物理模型。