【问题标题】:Controlling a moving non-kinematic rigidbody along a path控制沿路径移动的非运动刚体
【发布时间】:2019-08-18 16:44:03
【问题描述】:

我正在为 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);

}

【问题讨论】:

  • 对此有什么想法吗?这不是常见问题吗?
  • 只是问,为什么你不使用任何补间插件来像 DoTween 一样转动?,这将解决所有这些问题,除了它支持常规变换和刚体,你还可以简单的 DoTween.().Stop ..etc 如果您对此感兴趣,请告诉我,以便我为您写一个示例。
  • 正如我所说,我想在这个项目中使用物理学。

标签: c# unity3d rigid-bodies


【解决方案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 来解决这个问题,所以对你表示敬意。但是,当您向对象添加相对扭矩时,其影响在很大程度上取决于质量。

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

50,000 / (prior mass).

动态改变质量通常会导致问题。我建议找到一种不同的解决方案来迫使你的玩家在不涉及操纵质量的地面上(以及较小的关注点,重力)。也许将刚体.position 向下倾斜,或者施加向下的冲力可以达到相同的效果,而不会出现错误的风险。

第二

所有这些逻辑都发生在 FixedUpdate() 循环中。这与运行每一帧的 Update() 循环是分开的。我看到您正在访问一些特定于框架的数据,尤其是 transform.positiontransform.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 在这里也可能有用。

【讨论】:

  • 您好,埃里克,感谢您的回答。对于您的第一点,是的,我在跳跃后更改了快速着陆的质量,所以这仅在跳跃期间完成。我在没有跳跃的情况下跑步时会感到紧张,所以质量变化不应该是原因,而是你提出的一个很好的观点。可能是为了防止其他问题,例如跳跃过程中的侧移,我可能应该将其从对象顶部向下更改为Forcemode.Impulse。您的第二点实际上可能是问题的原因。我应该试试Rigidbody.position 看看。
  • 但是我想避免在非运动刚体上使用 .MovePosition.MoveRotation,并且只使用我相信 Unity 推荐的力。
  • 我试过Rigidbody.position,但没有任何作用。其他事情仍然是错误的。我的逻辑必须根据速度等完全改变。我可能在施加力时也弄错了向量。
  • 您还在 TurnToTarget 中读取 transform.eulerAngles(帧特定值)。角度让我的大脑受伤,但这也可能是引起紧张的原因。您能否提供视频或更详细地说明抖动的外观/行为? (它们是快速超快速抖动吗?它们仅在您开始转弯时发生,还是在整个过程中发生?它们是否在根本不转弯时发生?等等)
  • 这是一个常用功能,用于施加扭矩以将特定角度转向目标。我在网上找到的。如果我能找到,我会发布链接。如果您能提出更好的解决方案,我将不胜感激,因为这是发生抖动的关键。它施加的扭矩绰绰有余,当它超出目标方向时,它会在下一个固定更新帧处施加相反的扭矩。等等。即使我的问题没有解决,我还是奖励了你,因为你比其他回复更有洞察力。
猜你喜欢
  • 1970-01-01
  • 2017-10-31
  • 2014-02-17
  • 1970-01-01
  • 2011-09-03
  • 2011-05-26
  • 2011-08-20
  • 1970-01-01
  • 2022-01-13
相关资源
最近更新 更多