4

我正在为 Unity 开发一款适用于 Android 的无尽跑步游戏。我不想使用运动学刚体。因此涉及物理,但刚体应该默认沿着预定义的路径运行。(并通过用户操作跳跃或改变车道)。直线移动很容易。我已经做到了,但我希望在游戏的下一个阶段有回合。它似乎有效,但有时会变得紧张,并且转弯不像我想要的那样平滑。如果我提高速度,玩家就会变得不稳定。无论速度如何,您能否帮我优化代码以获得更顺畅的转弯。

据我搜索,我在互联网上找不到答案,可能人们更频繁地使用运动学刚体,而不是处理物理问题。所以我使用.AddForceand .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);

}
4

2 回答 2

3

你可以看看样条线。您可以通过计算当角色从一个点移动到另一个点时需要沿着该路径移动多少点才能使移动看起来平滑,从而减少沿路径移动角色的计算量。

当角色快速移动时,有时会使用模糊效果来减少需要绘制的多边形数量。

于 2019-08-25T21:38:53.250 回答
2

First

First thing to note is in this code:

    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);
    }

If looks like you're manipulating mass to achieve the affect of "stopping". If the player is in the air, you slam them into the ground with a high gravity value to slow them down rapidly. Manipulating mass of an object in motion can cause a great deal of issues, especially if you're applying a force in the same physics frame. I see you use ForceMode.VelocityChange to counter this problem, so kudos to you on that. However, when you AddRelativeTorque to an object, its impact depends heavily on the mass.

Changing the mass of an object directly does not automatically scale the current linear and angular momentum that an object has. Instead, when you increase the mass to 50,000f you are increasing the momentum by:

50,000 / (prior mass).

Changing mass on-the-fly is usually bound to cause problems. I would recommend finding a different solution to force your player to the ground that does not involve manipulating mass (and to a lesser concern, gravity). Perhaps lerping the rigidbody.position downard, or applying a downward impulse force may achieve the same effect without the risk for bugs.

Second

All of this logic is occurring in the FixedUpdate() cycle. This is separate from the Update() cycle that runs every frame. I see that you are accessing some frame-specific data, notably transform.position and transform.eulerAngles. It is possible that 2 physics cycles occur before the next frame cycle occurs, resulting in something like the following:

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

Since some of your game logic is based on the transform data, the FixedUpdate() can sometimes double-act before it can be updated, resulting in jittery movements.

I would recommend avoiding any transform reads in FixedUpdate(), and instead using RigidBody.position. Changing the rotation is a bit more nuanced, but RigidBody.MoveRotation may serve useful here as well.

于 2019-08-20T19:05:16.650 回答