快速移动球与鼠标控制的球拍的碰撞检测问题

Posted

技术标签:

【中文标题】快速移动球与鼠标控制的球拍的碰撞检测问题【英文标题】:Problem with collision detection of a fast moving ball with a racket controlled by mouse 【发布时间】:2011-09-09 16:47:00 【问题描述】:

在统一中,我有一个应该击球的球拍,球拍由鼠标直接控制,即鼠标使用鼠标轴移动球拍并使用 transform.translate() 函数移动球拍。

我预计 Unity3d 的物理不会直接通过鼠标正确转换球拍的运动并相应地撞击球,我必须编写一些自定义的东西,结果证明是真的。

但是当球拍移动时没有正确检测到球的碰撞。当它静止时,一切都很好,球的表现也如我所愿。

现在我编写了一个自定义物理脚本(我使用 C# 编写脚本),其中我将 4 个长度为 0.6F 的射线投射附加到球上,并在进行了一些复杂的矢量计算之后,计算球的速度击中球拍,并使用rigidbody.velocity = calculateVelocity() 将其直接应用于球的速度。现在,当球拍不移动时,它又可以正常工作了,但当我移动球拍时就不行了。问题的确切(症状)是:

使用内置物理和碰撞检测:当球拍移动时,球有时会直接穿过球拍,有时会减速(达到难以置信的水平)。

使用我的脚本计算速度:问题是一样的,但是当我打印对撞机(球拍)的法线时,它可以让我找出问题所在。它有时给出正确的法线,有时给出法线向量的负值,这意味着它直接穿过顶面并检测对撞机(球拍)底部的撞击。

我尝试过的事情:

    增加碰撞器的大小(它适用于球拍上较宽的盒子碰撞器,但显然球从距离球拍很远的地方移动,我自己的脚本在这里工作,默认物理给出奇怪球拍移动时的结果),总之我没有得到我想要的现实。

    将固定时间戳减少到 0.001,这显着改善了情况,但与我想要的结果仍然相去甚远,而且球再次经常选择错误的一侧。

    将碰撞检测更改为连续动态。这也没有改善。

除了在碰撞时选择了错误的一侧,我观察到的另一个问题是,球从球拍上弹起后,球在移动,但球拍移动得更快,它不是以某种方式沿着完整的弧线或直线移动出现在球的前面,造成两次安打。这是基于可见事物的猜想。

此外,很明显,Unity3d 的内置物理系统没有读取球拍的“运动”方面,导致当球拍移动时使用鼠标击球时出现奇怪的行为。

我被困住了,我不知道从这里搬到哪里。请告诉我我做错了什么。

【问题讨论】:

【参考方案1】:

正如其他人指出的那样,问题在于球从一帧中的垫子一侧变为下一帧的另一侧。如果障碍物太窄,快速移动的物体往往会这样做。

这个问题有三个非常简单的解决方案:

增加垫或球的大小,这就是您更改对撞机大小时发生的情况。 为小球设定一个最大速度,使其永远无法移动到足以穿过垫子的速度。 提高 Unity 进行物理计算的频率。可以在Time Manager中更改,减少Fixed Timestep的值。小心减少太多,否则物理引擎将无法在下一轮开始之前完成调用,并且永远无法赶上游戏。

为移动对象设置最大速度是必须始终完成的事情。你不能冒险让一个重要的物体在游戏过程中飞涨而让一切都处于不受控制的状态。

【讨论】:

我已经尝试为球拍和球设置最大速度,我可能会增加整个环境的大小,也许这可能会更好地检测对撞机的右侧。我将实现 Justin808 给出的解决方案并增加所有内容的大小。 这东西工作得非常出色,我把所有东西的大小都翻了一番,现在它没有错过任何碰撞。我还有 Justin808 谈到的另一个问题,即球拍在某一时刻在球的前面,而下一帧它在球的后面,但至少我看到了两个不同的碰撞,可以用代码处理!非常感谢!我只花了2分钟就完成了。现在我将实施贾斯汀的解决方案来解决两个碰撞。这个时候如果有人有其他解释,也请分享! 好的问题解决了,我不必在球的前一个和下一个位置之间放置一个射线,因为现在球拍的对撞机没有错过球。另一件事是,通过鼠标的球拍运动不会转化为施加在球上的额外力。我必须自己编写这部分,并将 itrigger 设置为真实的球拍对撞机,以便原始物理不会干扰。但是现在出现了多次击球的问题,我通过使用标志解决了它,第一次击球时变为假,1秒后再次变为真,或者当球击中另一个物体时。这行得通!【参考方案2】:

我认为正在发生的是,球/球拍发生的每个间隔都会移动,然后检查是否发生碰撞。问题是球/球拍在一个间隔内移动到很远而错过了碰撞。

1) Ball before racquet 
2) Ball after racquet 

not

1) Ball before racquet 
2) Ball touching racquet 

所以你要做的就是在你的球 GameObject 的 FixedUpdate() 方法中将一条射线从当前球的位置投射到前一个球的位置。如果这两个点之间有任何东西应该被击中(即球拍),则将球移回射线上报告的击球点。这将触发您现有的碰撞检测功能。

增加对撞机大小的原因也是因为球没有跳过较大的对撞机。这有你在问题中提到的缺点。光线投射避免了这个问题,并允许球/球拍根据需要快速或慢速移动。

【讨论】:

【参考方案3】:

这不是一个完整的答案,但我记得很多年前在较慢的机器上处理过类似的问题。

问题在于使用基于精灵的碰撞检测;依赖于在相同坐标处渲染的精灵和障碍物的像素。如果帧速率太低以至于精灵在一帧中移动的幅度超过了障碍物的大小,那么您将陷入(例如)在一帧中位于障碍物左侧而在下一帧中位于障碍物右侧的情况占用相同的像素。

在这种情况下,基于精灵的碰撞不起作用,您必须将碰撞基于向量。不要检查每个精灵像素是否有碰撞,而是记录精灵的位置和凸包。在每一帧之后,计算一个从旧位置到新位置的向量,并与每个障碍物的凸包相交。

您可以使用多种快捷方式,例如首先仅与边界框进行比较,并且仅当向量与边界框相交时才计算外壳,但这是基本思想。祝你好运。

【讨论】:

这个想法似乎是一个很好的起点,但是任何想法如何在 Unity3d 中使用尽可能多的内置东西来实现它(因为它是优化的)。关于慢速机器,我在具有 6 GB DDR3 RAM 和 1 GB ATi Radeon HD5890 显卡的核心 i7 机器上对其进行了测试,FPS 从未低于 70。请解释什么是基于 sprite 的碰撞检测,这是 Unity3d 使用的?由于我使用的是 Unity3d,我认为我无法将碰撞检测机制的细节控制到这种程度 抱歉,没用过Unity。基于 Sprite 的图形使用实际的帧缓冲区进行计算;如果您使用的是 OpenGL 或 ActiveX,您可能没有使用精灵。然而,检查向量与凸包碰撞的概念适用于保留模式图形。我想 Unity 有一种方法可以查看向量是否与船体相交。 这种方法在 Unity 中使用 3d 元素时是不可能的,因为碰撞是通过其内部引擎 PhysX 计算的。但这是一个很好的系统,已经在大多数物理引擎中实现。【参考方案4】:

我也一直在开发 3D 乒乓球游戏,并且遇到了完全相同的问题。我会像你们一样尝试扩大一切。至于桨给球增加速度和旋转,我对此感到困惑,直到我意识到改变桨的位置并不会改变它的速度。如果桨在移动之前的速度为零,那么当物理引擎在下一帧查看它时它将为零。 Unchecking 是 kenimatic 并且通过速度属性直接控制桨解决了这个问题。它导致桨在撞到墙壁时抖动,但我通过从桨层移除墙壁并在 LateUpdate 中手动处理边界来解决这个问题。此外,当您更新速度时,首先将新的所需速度存储在 Update 中,以便控件顺利工作,然后在 FixedUpdate 中提交更改。

【讨论】:

【参考方案5】:

您需要一个变量来消除球和球拍之间的碰撞。将变量初始化为真。在第一次与球拍碰撞时将变量设置为假。然后当球撞到球拍以外的东西时将其设置为真。当变量为假时忽略球拍碰撞。这将避免在球拍碰撞器的相对表面上发生多次连续碰撞。

【讨论】:

以上是关于快速移动球与鼠标控制的球拍的碰撞检测问题的主要内容,如果未能解决你的问题,请参考以下文章

JS:当球移动得更快时,碰撞检测不起作用

快速检测两个 UIView 的碰撞

使用旋转矩形进行碰撞检测

unity 用角色控制器怎么检测碰撞

Godot 物理引擎 2D

动力学js中的碰撞检测+鼠标事件