相机每隔几秒跟随背景对象的 Rigidbody 抖动一次

Aru*_*n K 1 c# unity-game-engine game-physics unity3d-2dtools

相机跟随 Rigidbody2D 每隔几秒抖动一次背景(非刚体)对象(障碍物)。Profiler 中的 FPS 很好,接近 100。插值也很好。使用Unity 2017.4.12(LTS)

GIF GIF 视频在这里

相机跟随脚本

 public class CameraFollow : MonoBehaviour {
        public float followRange = 0.5f;
        public float cameraZ;
        public Transform Player;
        public Vector3 newPos;
        void Start () {
            cameraZ = transform.position.z;
        }
        void FixedUpdate() {
            newPos = new Vector3(Player.position.x + followRange, 0, cameraZ);
            transform.position = Vector3.Lerp(transform.position, newPos, 0.5f);
        }   
    }
Run Code Online (Sandbox Code Playgroud)

播放器脚本:

public class PlayerBall : MonoBehaviour {

    public float xSpeed = 10;

    // Update is called once per frame
    void FixedUpdate () {
        this.transform.position = new Vector3(this.transform.position.x + Time.fixedDeltaTime * xSpeed, 
            this.transform.position.y , transform.position.z);
    }

}
Run Code Online (Sandbox Code Playgroud)

玩家刚体

在此输入图像描述

项目文件下载

小智 5

调用的速率与决定调用和FixedUpdate的速率的帧速率不同。UpdateLateUpdate

FixedUpdate应用于设置刚体的速度。设置位置FixedUpdate总是存在抖动的风险,因为它被称为与帧速率不同步。在模拟刚体上设置位置也违背了模拟它的目的,因为您忽略了物理可能对刚体产生的任何影响。

Ridgidbody 2D 的手册页还指出:

Rigidbody 2D 组件会覆盖 Transform,并将其更新为 Rigidbody 2D 定义的位置/旋转。请注意,虽然您仍然可以通过自己修改 Transform 组件来覆盖 Rigidbody 2D(因为 Unity 公开了所有组件上的所有属性),但这样做会导致游戏对象相互传递或相互传递以及不可预测的移动等问题。

[...]决不应该使用变换或任何碰撞体偏移直接移动2D 碰撞体;应该移动 Rigidbody 2D。这提供了最佳性能并确保正确的碰撞检测。

对于应该移动的 Rigidbody,您可以根据您的用例选择使用 aDynamic BodyType或 a 。Kinematic如果您希望飞机被其他非静态碰撞器推动,则它应该是动态的。如果它不应该四处移动,它应该是运动学的(这也更具性能)。

用例 1:动态刚体 2D

这是您当前的设置。然而,Ridgidbody 2D 的手册页还指出:

不要使用变换组件来设置动态刚体 2D 的位置或旋转。模拟根据动态刚体 2D 的速度重新定位它;您可以通过脚本施加的力直接改变它,或者通过碰撞和重力间接改变它。

因此,您应该更改 Player 脚本以使用 Rigidbody2D 方法AddForce

public class PlayerBall : MonoBehaviour {

  public float thrust = 10;
  Rigidbody2D body;
  Vector2 forwardDirection = Vector2.right;

  void Awake() {
    body = GetComponent<Ridigbody2d>();
  }

  // FixedUpdate is called once per physics update
  void FixedUpdate () {
    body.AddForce(forwardDirection * thrust)
  }

}
Run Code Online (Sandbox Code Playgroud)

现在,这将不断地向飞机施加力,推动其前进并加速。因此,您可能希望找到一种稳定速度的方法,方法是在接近目标速度时减少附加力(您可以使用 检查当前速度body.velocity.x)并使用 减慢飞机速度drag

我不会进一步讨论细节,因为我怀疑 Kinematic 实际上是您想要使用的:

用例 2:运动学刚体 2D

设置为 Kinematic 的 Ridgedbody2D可以使用 Ridgedbody2D 组件MovePositionBody Type方法安全地移动。因此,通过该设置,您的玩家脚本将如下所示: public class PlayerBall : MonoBehaviour {

  public float xSpeed = 10;
  Rigidbody2D body;
  Vector2 forwardDirection = Vector2.right;

  void Awake() {
    body = GetComponent<Ridigbody2d>();
  }

  // FixedUpdate is called once per physics update
  void FixedUpdate () {
    body.MovePosition(forwardDirection + xSpeed * Time.deltaTime)
  }

}
Run Code Online (Sandbox Code Playgroud)

注意:我在这里使用 Time.deltaTime 是因为此属性返回的值始终是其所使用的 Update 方法类型的正确增量时间。

跟随摄像头

更新相机位置应始终与帧速率同步。在FixedUpate 中这样做可能会导致卡顿。相反,它应该在 或 中完成Update(例如,如果您想等到由于动画应用而导致位置发生变化,然后再设置位置)LateUpdate

最后

如果游戏中的炸弹也在移动,并且是 Ridgidbodies,其位置在其固定更新中以类似方式更新,则您应该将它们更改为使用 2 个用例中任一用例中描述的解决方案。

请注意,运动刚体不会引起任何碰撞事件(仍会引起触发事件)如果您想将炸弹和飞机作为运动刚体移动,但仍会引起碰撞事件,您可以在应接收这些更新的刚体上进行Use Full Kinematic Contacts设置。我不确定这是否会对性能产生任何影响(除了生成这些事件的影响之外),您可能仍然想阅读1 [Ridigbody2D 组件的完整文档]。

总的来说,TL;DR:您所看到的可能不是性能问题(或者它会显示在 Profiler 中,并且您会出现抖动和较差的 FPS),而是由于使用错误的更新方法和方案来应用运动而导致的运动抖动到肉体。这是 Unity 中非常常见的陷阱。

如果存在性能问题,请附上分析器的屏幕截图。使用您正在使用的 FPS 显示方法,您可能会经常触发 GC.Collect 以进行 FPS 脚本(以及其他地方的 GC.Allocs)中的字符串操作。