沿路径控制运动的非运动刚体

时间:2019-08-18 16:44:03

标签: c# unity3d rigid-bodies

我正在研究Unity开发的适用于Android的无尽转轮游戏。我不想使用运动刚体。因此涉及物理,但默认情况下,刚体应沿预定路径运行。 (并通过用户操作来跳转或更改车道)。直线移动很容易。我已经做到了,但我想在转弯的比赛中进入下一个阶段。它似乎可以正常工作,但有时会出现抖动,转弯并不像我希望的那样平稳。而且,如果我提高速度,播放器就会变怪。不管速度如何,能否请我帮我优化代码以使转弯更平稳。

据我搜索,我在互联网上找不到答案,可能是人们更频繁地使用运动刚体,以便不理会物理学。因此,我使用.AddForce.AddTorque。现在,我使用具有预定转弯(预制件)的预制件。因此,它随着玩家的前进而生成。每个道路预制件都具有用于移动路径的样条线(我想是基于Unity 2015过程样条线生成视频的免费资产)。因此,玩家将沿着样条线拾取一个节点并将其设置为目标,并使用其旋转方向转向使用AddTorque。

如果我改用运动刚体,也许会更容易。也许这是理想的选择,但我坚持这样做是为了学习物理,并且由于没有足够的资源,有些人可能会发现它对另一个项目很有用。

void FixedUpdate()
  {


    if (!jump)
    {
        //maxangle = Mathf.Clamp(r.velocity.magnitude * 2f,3,15f);
        maxangle = r.velocity.magnitude;

        r.constraints = RigidbodyConstraints.None;
        r.constraints = RigidbodyConstraints.FreezeRotationZ | RigidbodyConstraints.FreezeRotationX;
        TurnToTarget(transform, sample.Rotation,target, maxangle);
        r.constraints = RigidbodyConstraints.None;
        r.constraints = RigidbodyConstraints.FreezeRotationZ | RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY;
    }
    //Debug.Log(currentroad.transform.name + maxangle);

    if (!GameManager.gameManager.dead  && running)
    {
        r.isKinematic = false;
        //Debug.Log(transform.position.y);
        var speed = r.velocity.magnitude;
        Vector3 directionOfTarget = (target - transform.position).normalized;

        if (speed < runspeed)
        {
            //r.velocity += Vector3.forward * 1f;
            Debug.Log(r.velocity.z+ " " + r.velocity.magnitude);
            Debug.Log(directionOfTarget);
            r.AddForce(directionOfTarget* (runspeed-speed), ForceMode.VelocityChange);
        }
        if (transform.position.y > 2.7f)
        {
            r.mass = 50000f;
            Physics.gravity = new Vector3(0, -100f, 0);
        }
        if (grounded)
        {
            r.mass = 10f;
            Physics.gravity = new Vector3(0, -10f, 0);
        }

private void TurnToTarget(Transform transform, Quaternion targetrot, Vector3 movePoint, float maxTurnAccel)
 {
      Vector3 directionOfTarget = (movePoint -transform.position).normalized;
      Vector3 directionInEulers = targetrot.eulerAngles;

      Vector3 offsetInEulers = ClampHeading(directionInEulers) - ClampHeading(transform.eulerAngles);
    offsetInEulers = ClampHeading(offsetInEulers);
    //optional

    Vector3 angularVelocity = r.angularVelocity / Time.fixedDeltaTime;
    if (offsetInEulers.sqrMagnitude < Mathf.Pow(maxTurnAccel, 2))
    {
        if (offsetInEulers.y < 0)
        {
            if (angularVelocity.y < offsetInEulers.y)
            {
                offsetInEulers.y = -offsetInEulers.y;
            }
        }
        else
        {
            if (angularVelocity.y > offsetInEulers.y)
            {
                offsetInEulers.y = -offsetInEulers.y;
            }
        }
        if (offsetInEulers.x > 0)
        {
            if (angularVelocity.x < -offsetInEulers.x)
            {
                offsetInEulers.x = -offsetInEulers.x * 2;
            }
        }
        else
        {
            if (angularVelocity.x > -offsetInEulers.x)
            {
                offsetInEulers.x = -offsetInEulers.x * 2;
            }
        }
        if (offsetInEulers.z > 0)
        {
            if (angularVelocity.z < -offsetInEulers.z)
                offsetInEulers.z = -offsetInEulers.z * 2;
        }
        else
        {
            if (angularVelocity.z > -offsetInEulers.z)
                offsetInEulers.z = -offsetInEulers.z * 2;
        }
    }
    offsetInEulers = ClampVector(offsetInEulers, -maxTurnAccel, maxTurnAccel);
    //Debug.Log(currentroad + " " + offsetInEulers + " " + r.angularVelocity + " " + directionOfTarget + " " + ClampHeading(directionInEulers)+" " +transform.eulerAngles);

    r.AddRelativeTorque(transform.up * offsetInEulers.y);
    //r.AddTorque(offsetInEulers*r.velocity.magnitude);

}

2 个答案:

答案 0 :(得分:3)

您可以查看样条曲线。您可以通过计算沿着角色在路径上移动字符所需的点,以使角色从一个点移动到另一个点,以使其看起来平滑。

有时,当人物快速移动时,模糊效果可用于减少需要绘制的多边形数量。

答案 1 :(得分:2)

第一

首先要注意的是这段代码:

    if (transform.position.y > 2.7f)
    {
        r.mass = 50000f;
        Physics.gravity = new Vector3(0, -100f, 0);
    }
    if (grounded)
    {
        r.mass = 10f;
        Physics.gravity = new Vector3(0, -10f, 0);
    }

如果您正在操纵质量以达到“停止”的效果。如果播放器处于空中,则以高重力值将它们猛撞到地面,以使其迅速减速。操纵运动中的物体的质量可能会导致很多问题,尤其是在同一物理框架中施加力的情况下。我看到您使用 ForceMode.VelocityChange 来解决此问题,因此您对此表示敬意。但是,当您向对象添加RelativeTorque时,其影响在很大程度上取决于质量。

直接更改对象的质量不会自动缩放对象具有的当前线性和角动量。相反,当您将质量增加到50,000f时,您将通过以下方式增加动量:

50,000 / (prior mass).

不断变化的质量通常会引起问题。我建议您找到一种不同的解决方案,将您的玩家逼到不涉及操纵质量(以及较小的重力)的位置。也许保持刚体的位置下降,或者施加向下的脉冲力可能会达到相同的效果,而不会出现漏洞。

第二

所有这些逻辑都在FixedUpdate()周期中发生。这与运行每帧的Update()周期是分开的。我看到您正在访问一些特定于帧的数据,特别是 transform.position transform.eulerAngles 。在下一帧周期发生之前可能会发生2个物理周期,结果如下:

Update() - transform is updated
FixedUpdate() - reads most recent transform data
Update() - transform is updated
FixedUpdate() - reads most recent transform data
FixedUpdate() - reads most recent transform data
Update() - transform is updated

由于您的某些游戏逻辑是基于变换数据的,所以FixedUpdate()有时在更新之前有时会发生双重作用,从而导致抖动。

我建议避免在FixedUpdate()中进行任何转换读取,而应使用RigidBody.position。更改旋转角度会更加细微,但是RigidBody.MoveRotation在这里也可能有用。