英文:
How to use Swift Scenekit Contact Test
问题
我正在开发一个游戏,用户可以使用手指移动物体,当用户释放手指时,游戏会检查物体之间是否重叠,如果有重叠,则将移动的物体移回到其原始位置。我正在使用SCNScene.PhysicsWorld.contactTest(with: )
来检查节点之间的重叠。然而,当我将物体的物理体形状从.convexHull
更改为.concavePolyHedron
时,一切都停止工作,不会报告任何接触。我已将物理体设置为静态,所以我不知道该怎么办。
这是我为每个节点配置物理体的代码:
parentNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(node: parentNode, options: [.type: SCNPhysicsShape.ShapeType.concavePolyhedron, .collisionMargin: 0.0, .scale: scaleVector]))
这是我调用接触测试的代码:
if let test = currentNode?.physicsBody {
let list = view.scene!.physicsWorld.contactTest(with: test) {
...
}
}
编辑:这是一个演示我的问题的基本游戏的GameViewController。当使用concavePolyHedron
时,碰撞检测不起作用,但将选项更改为convexHull
时,代码可以正常工作。
英文:
I am developing a game where users can use their finger to move objects, and when the user releases their finger the game checks for overlap between objects and move the moved object back to it's original position if there is overlap. I am using SCNScene.PhysicsWorld.contactTest(with: )
to check for overlap between my nodes. However, the method only works correctly when nodes have physicsbodys using .convexHull
, when I change it to .concavePolyHedron
everything stops working and no contact is reported. I have set the physicbodys to be static so I am at a loss of what to do.
Here is my code configuring the physicsbody for each node
parentNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(node: parentNode, options: [.type: SCNPhysicsShape.ShapeType.concavePolyhedron, .collisionMargin: 0.0, .scale: scaleVector]))
Here is my code calling contact test :
if let test = currentNode?.physicsBody {
let list = view.scene!.physicsWorld.contactTest(with: test) {
...
}
}
Edit: Here is a GameViewController of a basic game demonstrating my problem. The collision detection does not work with concavePolyHedron
but changing the option to convexHull
the code works correctly.
import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController {
var dragGesture: UIPanGestureRecognizer?
var currentNode: SCNNode?
var beginningPos: SCNVector3?
var previousLoc: CGPoint?
var scnView: SCNView?
override func viewDidLoad() {
super.viewDidLoad()
// create a new scene
let scene = SCNScene()
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
// retrieve the SCNView
scnView = self.view as! SCNView
// set the scene to the view
scnView?.scene = scene
// allows the user to manipulate the camera
scnView?.allowsCameraControl = true
// show statistics such as fps and timing information
scnView?.showsStatistics = true
// configure the view
scnView?.backgroundColor = UIColor.black
let node1 = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
node1.position = SCNVector3(x: 0, y: 0, z: 0)
node1.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(node: node1, options:
[.type: SCNPhysicsShape.ShapeType.concavePolyhedron, .collisionMargin: 0.0,]))
scene.rootNode.addChildNode(node1)
let node2 = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
node2.position = SCNVector3(x: 2, y: 2, z: 0)
node2.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(node: node2, options:
[.type: SCNPhysicsShape.ShapeType.concavePolyhedron, .collisionMargin: 0.0,]))
scene.rootNode.addChildNode(node2)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
scnView?.addGestureRecognizer(tapGesture)
}
func setupGesture() {
dragGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
view.addGestureRecognizer(dragGesture!)
}
func removeGesture() {
if let dragGesture = dragGesture {
view.removeGestureRecognizer(dragGesture)
self.dragGesture = nil // Set the dragGesture to nil after removing it
}
}
@objc
func handlePan(_ sender: UIPanGestureRecognizer) {
var delta = sender.translation(in: self.view)
let loc = sender.location(in: self.view)
if sender.state == .began {
if let location = currentNode?.position {
beginningPos = location
previousLoc = loc
}
}
if sender.state == .changed {
if let currentNode {
delta = CGPoint.init(x: 2 * (loc.x - previousLoc!.x), y: 2 * (loc.y - previousLoc!.y))
currentNode.position = SCNVector3.init(currentNode.position.x + Float(delta.x * 0.0075), currentNode.position.y - Float(delta.y * (0.0075)), currentNode.position.z)
}
previousLoc = loc
}
if sender.state == .ended {
if let test = currentNode?.physicsBody {
let list = scnView?.scene?.physicsWorld.contactTest(with: test)
if list!.count > 0 {
if let beginningPos {
currentNode?.position = beginningPos
}
}
}
}
}
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView?.hitTest(p, options: [:])
if let node = currentNode {
removeGesture()
scnView?.allowsCameraControl = true
if let material = node.geometry?.firstMaterial {
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
material.multiply.contents = UIColor.white
SCNTransaction.commit()
}
currentNode = nil
}
// check that we clicked on at least one object
if hitResults!.count > 0 {
// retrieved the first clicked object
currentNode = hitResults!.first?.node
scnView?.allowsCameraControl = false
setupGesture()
let pos = currentNode!.position
if let material = currentNode?.geometry?.firstMaterial {
// highlight it
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
material.multiply.contents = UIColor.red
SCNTransaction.commit()
}
}
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
}
答案1
得分: 0
的确,convexHull
和 concavePolyhedron
的行为在这里有点奇怪。
我猜想,你的目标是将一个立方体放在现有的立方体上方(或其他位置)。根据你的配置,表面的边缘超出了实际立方体大小的微小距离,因此即使没有触碰现有立方体,立方体也会翻转到其原始位置。我稍微尝试了一下,这是我取得的最佳结果:
let node1 = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
node1.position = SCNVector3(x: 0, y: 0, z: 0)
node1.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: node1.geometry!))
scene.rootNode.addChildNode(node1)
let node2 = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
node2.position = SCNVector3(x: 2, y: 2, z: 0)
node2.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: node2.geometry!))
scene.rootNode.addChildNode(node2)
正如你所看到的,我没有对 physicsBody
进行特殊的配置。现在我能够将这两个立方体尽可能地靠拢。
使用:SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: node2.geometry!))
希望这对你有所帮助。
英文:
Indeed, the behaviour of that convexHull
and concavePolyhedron
are a little bit strange here.
I suppose, your goal is to place one cube like on top of the existing one (or where ever.) With your config the margin at the surface overlapped the real cube size by a tiny distance, so the cube flips to its original position, even when it did not touch the existing cube. I played around a little bit and this is my best result, I achieved:
let node1 = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
node1.position = SCNVector3(x: 0, y: 0, z: 0)
node1.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: node1.geometry!))
scene.rootNode.addChildNode(node1)
let node2 = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
node2.position = SCNVector3(x: 2, y: 2, z: 0)
node2.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: node2.geometry!))
scene.rootNode.addChildNode(node2)
As you can see, I do not force some special configuration to the physicsBody
. And this is the most close, I can move the cubes together now.
Using: SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: node2.geometry!))
I hope it will help.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论