Unity, Rigidbody.MovePosition() 如果 transform.position 只是被更改,



问题

此代码有效。 ↓

private void FixedUpdate()
{
Vector3 newPosition = new Vector3(0, 1, 0);
GetComponent<Rigidbody>().MovePosition(newPosition);
}

此代码不起作用。

private void FixedUpdate()
{
Vector3 newPosition = new Vector3(0, 1, 0);
Vector3 oldPosition = transform.position;
transform.position = newPosition;
transform.position = oldPosition;
GetComponent<Rigidbody>().MovePosition(newPosition);
}

我在 Unity 2019.4 和 2020.3 中进行了测试。

所以,如果transform.position只是改变了,Rigidbody.MovePosition()似乎将无法工作.
为什么会这样?


2021.9.3 添加

我发现我可以使用Physics.SyncTransforms()来解决这个问题。

此代码有效。 ↓

private void FixedUpdate()
{
Vector3 newPosition = new Vector3(0, 1, 0);
Vector3 oldPosition = transform.position;
transform.position = newPosition;
transform.position = oldPosition;
Physics.SyncTransforms();  //newly added
GetComponent<Rigidbody>().MovePosition(newPosition);
}

2023.9.2 新增

【第一部分】

两年过去了。这些天我碰巧又在处理物理。

我了解到,如果我想直接改变位置,Rigidbody.position 比 Transform.position 更好.
如果使用得当,Rigidbody.position 不会导致 Rigidbody.MovePosition() 不起作用。

正如@thekirmizi所说,通过Transform.positionRigidbody.MovePosition()移动对象与最佳实践相去甚远。

如果我想以连续/物理方式移动物体,我可以使用Rigidbody.velocityRigidbody.AddForceRigidbody.MovePosition.
连续/物理方式意味着物体在移动时可能会与其他刚体碰撞,然后不会移动到目的地。

如果我想以传送方式移动物体,我可以使用Transform.postionRigidbody.position.
通过这种方式,物体从一个地方传送(或跳跃)到另一个地方。

连续方式和瞬移方式很少会结合在一起。 如果我对 Unity 的物理系统很感兴趣,我应该在编码之前选择一种方式.
(当然,2 年前我不是物理系统的农夫,所以我来到了我想同时使用两种方式的尴尬角落。

【第二部分】

最后,为什么变换位置变化会导致刚体>。MovePosition 不起作用?
我不能确定,但我现在可以做一些猜测。

内部物理更新在执行顺序中是物理(例如物理移动,碰撞等)发生的真实位置.
也就是说,如果我运行Rigidbody.MovePosition,刚体不会直接移动,它将在内部物理更新中移动。在内部物理更新之前,物体仍处于旧位置,在内部物理更新之后,它被移动到新位置。

我想,如果我运行Rigidbody.MovePosition(newPosition),Unity会从我那里得到一个命令,即将对象从oldPosition移动到newPosition。oldPosition 和 newPosition 都很重要.
(这实际上是一个没有证据的猜测。它有很大的机会犯错。它唯一的好处是它可以帮助我理解为什么Transform.move与Rigidbody.MovePosition不兼容。

在内部物理更新中,当Unity即将执行Rigidbody.MovePosition时,他会检查对象是否仍在旧位置。顺序是将其从 oldPosition 移动到 newPosition,如果它不再处于 oldPosition,Unity 会认为该订单已过时,并且不会执行 Rigidbody.MovePosition。

oldPosition 和 newPosition 的位置是物理位置,即 Rigidbody.position.
所以如果代码 Rigidbody.MovePosition 在time1运行,则内部物理更新实际上在time2执行 Rigidbody.MovePosition。Rigidbody.position 在 time1 和 time2.
之间必须保持不变,所以Rigidbody.position = oldPosition2; Rigidbody.MovePosition(newPositon); //Internal Physics Update是可以的,因为 Rigidbody.position 在 Rigidbody.MovePosition 之前更改,并且 time1 和 time2.
之间不会有任何变化Rigidbody.MovePosition(newPositon); Rigidbody.position = oldPosition2; //Internal Physics Update但将导致 MovePosition 无法正常工作。刚体位置在时间 1 和时间 2 之间更改。

让我们谈谈Transform.position = oldPosition2; Rigidbody.MovePosition(newPositon); //Internal Physics Update.
Transform.position = oldPosition2使 Transform.position 和 Rigidbody.position 具有不同的值,而 Transform.position 的值较新。 我想在内部物理更新的第一阶段,Unity 将同步 Transform.position 和 Rigidbody.position。如果 Transform.position 具有较新的值,则会为 Rigidbody.position 分配较新的值,即示例中的 oldPosition2
然后,当它即将执行 MovePosition 时,Unity 发现 Rigidbody.position 发生了变化,因此它没有被执行.
这意味着Transform.position = oldPosition2; Rigidbody.MovePosition(newPositon); //Internal Physics Update等于Transform.position = oldPosition2; Rigidbody.MovePosition(newPositon); Rigidbody.position = Transform.position; //The left of Internal Physics Update.
Rigidbody.position 在 time1 和 time2 之间更改。

Transform.position = oldPosition2; Physics.SyncTransforms(); Rigidbody.MovePosition(newPositon);//Internal Physics Update等于Transform.position = oldPosition2; Rigidbody.position = Transform.position; Rigidbody.MovePosition(newPositon); //Internal Physics Update.
Rigidbody.position 在 time1 和 time2 之间没有变化。 因此,Physics.SyncTransforms() 可以帮助使 Transform.position 和 Rigidbody.MovePosition 兼容。

结语:

Rigidbody.position = oldPosition2; Rigidbody.MovePosition(newPositon);

Rigidbody.MovePosition(newPositon); Rigidbody.position = oldPosition2;

Transform.position = oldPosition2; Rigidbody.MovePosition(newPositon);

Transform.position = oldPosition2; Physics.SyncTransforms(); Rigidbody.MovePosition(newPositon);

就这样。这是我的猜测和2年后的理解。

首先,Rigidbody 的 MovePosition 方法只应在不断检查碰撞/触发器时使用,前提是附加到游戏对象的刚体是否具有连续/离散碰撞检测。如果您确实没有在项目中使用太多物理场,请避免使用它,而是使用其变换来移动对象。

对象不移动的原因是(至少我假设这是您在屏幕上看到的)是您尝试将对象的转换位置和刚体设置为相同的 FixedUpdate 时间戳,并且在FixedUpdate上更新转换与任何最佳实践相去甚远。如果将编辑转换位置的行移动到Update方法,它可能会移动对象(取决于运行游戏的 FixedUpdate 的帧速率和时间戳)。

我的建议是,要么停止在 FixedUpdate 上更新转换的位置,要么只使用 rb。MovePosition 方法,或删除 FixedUpdate 方法并更新转换在 Update() 方法上的位置。

另外,请确保暂时不要在该 MonoBehavior 类中的其他任何地方更改 transform.position、rb.position 或 rb.velocity,只是为了确保这段代码工作正常。

Physics2D.SyncTransforms(); 这个函数对我有用... 我需要使用游戏对象的 Transform.Translation,然后将 Rigibody.MovePosition 用于子游戏对象。孩子如何接受帕伦德的翻译,rb。移动位置只是不起作用...在两个功能之间,物理的更新就足够了。 Physics2D.SyncTransforms() 允许更新 transform.translation 的物理特性。

相关内容

最新更新