Scenekit - 物理世界设置以防止运动学节点相交 [英] Scenekit - physicsWorld setup to prevent kinematic nodes to intersect
问题描述
我的场景中有几个节点,我可以旋转和拖动它们.我已经设置了一个physicsWorld 委托,向我的节点添加了带有运动学类型的physicsBody 并检查了physicsWorld didBeginContact - 到目前为止一切正常,当我移动节点时,接触开始/结束触发.
I have several nodes in my scene which i can rotate and drag around. I've set up a physicsWorld delegate, added physicsBody with type kinematic to my nodes and check for physicsWorld didBeginContact - up to this point everything is ok, when i move the nodes , the contact begin/end fires.
我试图处理这个问题的方法是设置一个 bool var,一旦接触开始,该变量就会变为 true 以防止进一步移动,但我遇到了一些情况(特别是如果我拖动节点太快),那该节点进入另一个对象的内部.
The way i'm trying to handle this is to set a bool var that change to true once the contact start to prevent further movement, but i'm running into cases (especialy if i drag the node too fast) , that the node gets a little bit inside of the other object.
我应该使用不同的方法吗?我真的不想要碰撞,只是让另一个节点表现得稳固",即使在高速接触时也不允许交叉.
should i use different approach to this? I don't really want a collision, just the other node to act "solid" and not allow intersection even at higher speed contact.
一些示例图像以进一步阐明问题:
Some sample images to further clarify the issue:
为简单起见,我仅添加了 2 个节点来演示该问题.第一张图片是初始位置,第二张和第三张(侧视图)在向右快速平移之后.只有在节点已经相交后才会触发接触检测.
For simplicity i've added only 2 nodes to demonstrate the issue. First image is initial position, 2nd and 3rd (side view) are after a very fast pan to the right. The contact detection was fired only after nodes were already intersected.
我尝试过的一种方法是在触发接触之前抓取最后一个位置并在检测到接触后重置节点位置,但结果非常不稳定且不稳定,有时您有时可以看到物体在之前相交跳到最后一个好"位置.我觉得必须有一些更简单的方法来实现这一点,但在花了几个小时浏览可用资源后,我无法弄清楚.
One approach that I tried was grabbing the last position before the contact is triggered and reseting the node position after contact is detected, but the result is very choppy and unstable and for a split second you can sometimes see the objects intersecting anyway before jumping to the last "good" position. I feel there must be some easier way to achieve this, but after spending hours going through available resources i can't figure it out.
编辑 2
进一步的研究指出了扫描测试的路线,技术上如果我可以在移动节点之前检测到可能的碰撞,我应该能够在交叉发生之前限制移动停止
further research pointed down the line of sweep testing, technicaly if i can detect possible collision before i move the node, i should be able to restrict the movement to stop before the intersetion occurs
更新:另一个死胡同,正如 Xcode 指出的
UPDATE: another dead end, as Xcode points out
Error: convexSweep only works with convex shapes
推荐答案
在折腾了好几天几乎要放弃之后,我重新阅读了physicsWorld 的文档,终于找到了我一直忽略的东西——可以随时手动触发的 contactTest 方法,独立于渲染循环.我在渲染器(_:willRenderScene:atTime:) 中使用它,以便在渲染场景之前修复"重叠.
After a lot of days of headbanging and almost giving up, i re-re-reread the physicsWorld documentation and finally found what i was overlooking all the time - the contactTest method that can be manualy triggered at any time, independent of render loop. I use it in the renderer(_:willRenderScene:atTime:) in order to "fix" the overlap before the scene gets rendered.
我的场景比示例要复杂一些,但是我几乎用了几个 tweek 就可以开始工作了.我不确定这是否是正确的解决方案,以及它在性能方面的成本有多高,但现在我会解决这个问题,以便我可以继续开发.
My scene is bit more complicated than the example, but with few extra tweeks i almost got it working now. I'm not sure if this is the proper solution and how costy it will become performance wise, but for now i will settle down with this so i can move on with the development.
以防有人在类似情况下运行的相关代码:
The relevant code in case somebody runs in similar situation:
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
// make sure we have active node and pan direction
if(selectedBrickNode != nil && self.panDirection != nil){
// contactTest
let pw = scnScene.physicsWorld
let node = selectedBrickNode.node!
let contacts = pw.contactTest(with: node.physicsBody!, options: nil)
var axisVector:SCNVector3
// specify which axis we want to correct
switch self.panDirection!{
case "right","left": axisVector = SCNVector3Make(1,0,0)
default: axisVector = SCNVector3Make(0,1,0);
}
for contact in contacts {
// round contact normal to get a unit vector
let cn = SCNVector3( round(contact.contactNormal.x),
round(contact.contactNormal.y),
round(contact.contactNormal.z))
// fix only for pan direction axis
if abs(cn.x) == axisVector.x && abs(cn.y)==axisVector.y {
let normal = contact.contactNormal
let transform = SCNMatrix4MakeTranslation( round(normal.x) * -Float(contact.penetrationDistance),
round(normal.y) * -Float(contact.penetrationDistance),
round(normal.z) * -Float(contact.penetrationDistance))
node.transform = SCNMatrix4Mult(node.transform, transform)
// break to prevent repeated contacts
break;
}
}
}
}
这篇关于Scenekit - 物理世界设置以防止运动学节点相交的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!