【问题标题】:Raycasting an instantly moved collider光线投射一个瞬间移动的对撞机
【发布时间】:2016-11-15 12:18:10
【问题描述】:

我需要将一些带有附加碰撞器的变换移动到特定位置,然后检查其中一个是否被光线投射击中。

我以天真的方式(伪代码)做到了:

foreach(object in objects){
   actual_position = object.transform.position
   object.transform.position = object.new_position
}
if(Physics.Raycast(...)) objectHit();
// Then I revert each of them them back to their actual_position

在使用相同的上下文(每个对象的测试之间的位置相同)进行多次测试后,光线投射有时会丢失,有时不会 (~50/50)。

做了一些研究,发现在 Raycast 文档页面:

如果您通过脚本或动画移动对撞机,则需要 至少执行一个 FixedUpdate 以便物理库可以 更新它的数据结构,在 Raycast 击中对撞机之前 这是新职位。

于是我平息了自己的怒火,开始寻找解决办法。 该线程有一种使用协程等待下一个滴答声的方法: Raycast doesn't work properly

我担心它对我不起作用,因为我需要这些物体立即回到它们的真实位置。 这个过程每帧可以发生多次(每次玩家开火)

有没有办法强制对撞机更新?

如果不是...我应该自己制作光线投射和对撞机吗? :(

谢谢

【问题讨论】:

  • 您是否尝试过使用类似 Bounds.IntersectRay 的东西?如果您的对象上有一个简单的碰撞器,(或者您的光线投射不需要 100% 准确)对于像盒子碰撞器这样简单的东西,您可以执行 object.GetComponent<Collider>().bounds 来获取边界,并将边界对象移动到新的位置,检查它,然后只在没有碰撞的情况下移动对象,这在性能上也比移动两次更好,因为如果它不应该移动它甚至根本不会移动它,docs.unity3d.com/ScriptReference/Bounds.IntersectRay.html
  • 看起来像一个聪明的解决方案,我的对撞机目前是简单的胶囊,但我想这也适用于更复杂的 MeshCollider。我看到的唯一问题是我必须对每个对象进行测试,并确保它们与射线源之间没有墙。这比仅仅抛出一个光线投射更复杂,但为什么不呢。非常感谢您的帮助。
  • 看着你链接的答案,我想也许你可以尝试使用yield return new WaitForFixedUpdate();,因为它会在每次物理更新之前被调用?
  • @FLX 如果这就是您的意思,您不必对每个世界对象进行测试,对于您要移动的每个对象,您只需执行一次 BoxCast 检查(或 CapsuleCast in你的情况),这两者放在一起仍然比实际使用带有刚体的对撞机效率高很多倍(更不用说实际移动物体两次) - CapsuleCast 第一次有点难以掌握,但当你正确理解它们
  • @Kardux 我认为它在这种情况下不起作用:我可以同时进行多次倒带,如果我需要等待下一个刻度,我担心某些位置可能会覆盖另一个位置。我对例程不太满意,所以我可能错了,但设计仍然过于复杂。

标签: unity3d


【解决方案1】:

另一种解决方法是立即停用并激活 Gameobject(附加对撞机)。在这种情况下,碰撞器位置将在单帧中更新。 另一种解决方案是 Physics.autoSyncTransforms and Physics.SyncTransform

【讨论】:

  • 如果您正在制作 2D 游戏,请确保使用 Physics2D.SyncTransform()。
【解决方案2】:

也许你可以试试这个(添加到你的伪代码中):

foreach(object in objects)
{
   actual_position = object.transform.position;
   object.transform.position = object.new_position;

   StartCoroutine(CheckToRevertOnNextFixedUpdate(object, actual_position));
}

IEnumerator CheckToRevertOnNextFixedUpdate(object, actual_position)
{
   yield return new WaitForFixedUpdate();

   if(Physics.Raycast(...)) objectHit();
   // Then I revert each of them them back to their actual_position
}

本质上,这会将您的检查延迟到每个对象的下一个 FixedUpdate() - 并在需要时恢复每个对象。我希望这不会过于复杂,因为您只需添加几行代码。

我还假设将对象的位置移动 1 个 FixedUpdate() 帧不会产生对象传送到新位置并返回的视觉效果。但是,您始终可以只移动对撞机,然后在 FixedUpdate() 之后将其余的变换移动到那里。

【讨论】:

  • 这就是他们在链接线程中的做法,但如果另一个玩家在移动时射击,那么实际位置可能是错误的(我说得对吗?)。此外,Paradox 的解决方案在性能方面似乎更好
  • @FLX 使用协程的想法与该线程相似,但细节有所不同,适用于您的上下文。如果另一个玩家在移动时射击,这无关紧要,因为碰撞器位置尚未在下一个 FixedUpdate() 中更新。是的,Paradox 的解决方案可能会更有效,如果您可以将您的对撞机形状近似为一个边界框,并且这样做的过程不会产生大量开销
【解决方案3】:

就性能而言,最好的方法似乎是更新RigidBody.position

private Rigidbody Rigidbody;

void Start()
{
    Rigidbody = gameObject.GetComponent<Rigidbody>();
}

void Upate()
{
    //.... your code
 
    Rigidbody.position = newPosition;
}

比停用/激活或Physics.SyncTransform()快得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 2016-09-07
    • 2019-11-18
    • 2012-11-07
    相关资源
    最近更新 更多