当两个对象在 contact/collides 中时尝试打印一些东西。稍后会在球触及桶底时增加得分
Trying to print something when two objects are in contact/collides. Will later add score when ball touches the bottom of the bucket
我的两个对象是:一个球和一个水桶。桶由两个 childNode 组成 我想要实现的是每当球进入桶内并触及作为基础的桶的 "cylinder" 时做一些事情。但是当我将 baseNode 添加为名称为 cylinder 的 childNode 时,我的桶消失在场景中并且不加载任何东西。我怎样才能使它与正在发生的联系一起工作。
import UIKit
import SceneKit
import ARKit
enum BodyType: Int{
case ballNode = 1
case baseNode = 2
}
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
@IBOutlet weak var AddBasketBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene.physicsWorld.contactDelegate = self as? SCNPhysicsContactDelegate
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene
let scene = SCNScene()
// Set the scene to the view
sceneView.scene = scene
registerGestureRecognizer()
}
func registerGestureRecognizer(){
let directions : [UISwipeGestureRecognizer.Direction] = [.up]
for direction in directions{
let swipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
swipe.direction = direction
sceneView.addGestureRecognizer(swipe)
}
}
@objc func handleSwipe(gestureRecognizer: UISwipeGestureRecognizer){
//print ("will it work?")
//sceneview to be accessed
//access the point of view of the sceneView, the center point
guard let sceneView = gestureRecognizer.view as? ARSCNView else{
return
}
guard let centerPoint = sceneView.pointOfView else {
return
}
//transform matrix
//the orientation
//position of camera
// orientation and position are needed to determine the camera where the ball needs to be placed
let cameraTransform = centerPoint.transform
let cameraLocation = SCNVector3(x: cameraTransform.m41, y: cameraTransform.m42, z: cameraTransform.m43)
let cameraOrientation = SCNVector3(x: -cameraTransform.m31, y: -cameraTransform.m32, z: -cameraTransform.m33)
//x1 + x2, y1+y2, z1+z2
let cameraPosition = SCNVector3Make(cameraLocation.x + cameraOrientation.x, cameraLocation.y + cameraOrientation.y, cameraLocation.z + cameraOrientation.z)
let ball = SCNSphere(radius:0.04)
// Bucketnode.scale = SCNVector3Make(0.2,0.2,0.2);
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "basketballSkin.png")
ball.materials = [material]
let ballNode = SCNNode(geometry: ball)
ballNode.position = cameraPosition
let physicsShape = SCNPhysicsShape(node: ballNode, options:nil)
let physicsBody = SCNPhysicsBody(type: .dynamic, shape: physicsShape)
ballNode.physicsBody = physicsBody
ballNode.physicsBody?.categoryBitMask = BodyType.ballNode.rawValue
ballNode.physicsBody?.collisionBitMask = BodyType.ballNode.rawValue
ballNode.physicsBody?.contactTestBitMask = BodyType.ballNode.rawValue
let forceVector:Float = 2.7
ballNode.physicsBody?.applyForce(SCNVector3Make(cameraPosition.x * forceVector, cameraPosition.y * forceVector, cameraPosition.z*forceVector), asImpulse: true)
sceneView.scene.rootNode.addChildNode(ballNode)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // change 2 to desired number of seconds
ballNode.removeFromParentNode()
}
}
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
let contactMask = contact.nodeA.categoryBitMask | contact.nodeB.categoryBitMask
switch (contactMask) {
case BodyType.baseNode.rawValue | BodyType.ballNode.rawValue :
print("hit")
default:
return
}
}
func addBackboard(){
guard let bucketScene = SCNScene(named:"art.scnassets/BucketBlue.scn") else {
return
}
guard let bucketNode = bucketScene.rootNode.childNode(withName: "tube", recursively: false) else {
return
}
guard let baseNode = bucketScene.rootNode.childNode(withName: "cylinder", recursively: true) else {
return
} //this part makes my bucket disappear, without it, it works fine but no contact action can be recognised
baseNode.physicsBody?.categoryBitMask = BodyType.baseNode.rawValue
baseNode.physicsBody?.collisionBitMask = BodyType.baseNode.rawValue
baseNode.physicsBody?.contactTestBitMask = BodyType.baseNode.rawValue
bucketNode.scale = SCNVector3Make(0.15,0.15,0.15);
bucketNode.worldPosition = SCNVector3(x: 0, y: -1.35, z: -1.4)
let physicsShape = SCNPhysicsShape(node: bucketNode, options: [SCNPhysicsShape.Option.type: SCNPhysicsShape.ShapeType.concavePolyhedron])
let physicsBody = SCNPhysicsBody(type: .static, shape: physicsShape)
bucketNode.physicsBody = physicsBody
sceneView.scene.rootNode.addChildNode(bucketNode)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
// MARK: - ARSCNViewDelegate
/*
// Override to create and configure nodes for anchors added to the view's session.
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
return node
}
*/
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
@IBAction func AddBasket(_ sender: Any) {
addBackboard()
AddBasketBtn.isHidden = true
}
}
几件事,首先是 ARKit space 以米为单位,所以在放置对象时请记住这一点,其次你并没有真正使用 ARkit(也许以后会用到它,好的)但是如果这是你的计划,那么使用锚点创建 scnnode 然后将你的桶放在锚点之上可能是个好主意:
接下来的代码主要来自Apple的开发者网站
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
// `SCNPlane` is vertically oriented in its local coordinate space, so
// rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.25
// Add the plane visualization to the ARKit-managed node so that it tracks
// changes in the plane anchor as plane estimation continues.
node.addChildNode(planeNode)
node.addChildNode(bucketNode)
}
因此,您首先允许 ARKit 创建锚点,然后放置 SCNPlane,然后您将在 SCNPlane 的中间看到您的水桶(只是不要为您的水桶设置任何位置)。
最后将 SCNTorus 定位在 float3(0.0,0.20,0.0) 这样的地方,这是 20 厘米。在锚的中心上方
我在代码中解决了我的问题,方法是在 SceneKit 编辑器中删除桶的基部,然后使用 SCNCylinder 作为场景的新节点通过代码向其添加另一个基部。然后我使用新节点通过接触位掩码与我的球接触。
我的两个对象是:一个球和一个水桶。桶由两个 childNode
import UIKit
import SceneKit
import ARKit
enum BodyType: Int{
case ballNode = 1
case baseNode = 2
}
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
@IBOutlet weak var AddBasketBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene.physicsWorld.contactDelegate = self as? SCNPhysicsContactDelegate
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene
let scene = SCNScene()
// Set the scene to the view
sceneView.scene = scene
registerGestureRecognizer()
}
func registerGestureRecognizer(){
let directions : [UISwipeGestureRecognizer.Direction] = [.up]
for direction in directions{
let swipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
swipe.direction = direction
sceneView.addGestureRecognizer(swipe)
}
}
@objc func handleSwipe(gestureRecognizer: UISwipeGestureRecognizer){
//print ("will it work?")
//sceneview to be accessed
//access the point of view of the sceneView, the center point
guard let sceneView = gestureRecognizer.view as? ARSCNView else{
return
}
guard let centerPoint = sceneView.pointOfView else {
return
}
//transform matrix
//the orientation
//position of camera
// orientation and position are needed to determine the camera where the ball needs to be placed
let cameraTransform = centerPoint.transform
let cameraLocation = SCNVector3(x: cameraTransform.m41, y: cameraTransform.m42, z: cameraTransform.m43)
let cameraOrientation = SCNVector3(x: -cameraTransform.m31, y: -cameraTransform.m32, z: -cameraTransform.m33)
//x1 + x2, y1+y2, z1+z2
let cameraPosition = SCNVector3Make(cameraLocation.x + cameraOrientation.x, cameraLocation.y + cameraOrientation.y, cameraLocation.z + cameraOrientation.z)
let ball = SCNSphere(radius:0.04)
// Bucketnode.scale = SCNVector3Make(0.2,0.2,0.2);
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "basketballSkin.png")
ball.materials = [material]
let ballNode = SCNNode(geometry: ball)
ballNode.position = cameraPosition
let physicsShape = SCNPhysicsShape(node: ballNode, options:nil)
let physicsBody = SCNPhysicsBody(type: .dynamic, shape: physicsShape)
ballNode.physicsBody = physicsBody
ballNode.physicsBody?.categoryBitMask = BodyType.ballNode.rawValue
ballNode.physicsBody?.collisionBitMask = BodyType.ballNode.rawValue
ballNode.physicsBody?.contactTestBitMask = BodyType.ballNode.rawValue
let forceVector:Float = 2.7
ballNode.physicsBody?.applyForce(SCNVector3Make(cameraPosition.x * forceVector, cameraPosition.y * forceVector, cameraPosition.z*forceVector), asImpulse: true)
sceneView.scene.rootNode.addChildNode(ballNode)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // change 2 to desired number of seconds
ballNode.removeFromParentNode()
}
}
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
let contactMask = contact.nodeA.categoryBitMask | contact.nodeB.categoryBitMask
switch (contactMask) {
case BodyType.baseNode.rawValue | BodyType.ballNode.rawValue :
print("hit")
default:
return
}
}
func addBackboard(){
guard let bucketScene = SCNScene(named:"art.scnassets/BucketBlue.scn") else {
return
}
guard let bucketNode = bucketScene.rootNode.childNode(withName: "tube", recursively: false) else {
return
}
guard let baseNode = bucketScene.rootNode.childNode(withName: "cylinder", recursively: true) else {
return
} //this part makes my bucket disappear, without it, it works fine but no contact action can be recognised
baseNode.physicsBody?.categoryBitMask = BodyType.baseNode.rawValue
baseNode.physicsBody?.collisionBitMask = BodyType.baseNode.rawValue
baseNode.physicsBody?.contactTestBitMask = BodyType.baseNode.rawValue
bucketNode.scale = SCNVector3Make(0.15,0.15,0.15);
bucketNode.worldPosition = SCNVector3(x: 0, y: -1.35, z: -1.4)
let physicsShape = SCNPhysicsShape(node: bucketNode, options: [SCNPhysicsShape.Option.type: SCNPhysicsShape.ShapeType.concavePolyhedron])
let physicsBody = SCNPhysicsBody(type: .static, shape: physicsShape)
bucketNode.physicsBody = physicsBody
sceneView.scene.rootNode.addChildNode(bucketNode)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
// MARK: - ARSCNViewDelegate
/*
// Override to create and configure nodes for anchors added to the view's session.
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
return node
}
*/
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
@IBAction func AddBasket(_ sender: Any) {
addBackboard()
AddBasketBtn.isHidden = true
}
}
几件事,首先是 ARKit space 以米为单位,所以在放置对象时请记住这一点,其次你并没有真正使用 ARkit(也许以后会用到它,好的)但是如果这是你的计划,那么使用锚点创建 scnnode 然后将你的桶放在锚点之上可能是个好主意:
接下来的代码主要来自Apple的开发者网站
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
// `SCNPlane` is vertically oriented in its local coordinate space, so
// rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.25
// Add the plane visualization to the ARKit-managed node so that it tracks
// changes in the plane anchor as plane estimation continues.
node.addChildNode(planeNode)
node.addChildNode(bucketNode)
}
因此,您首先允许 ARKit 创建锚点,然后放置 SCNPlane,然后您将在 SCNPlane 的中间看到您的水桶(只是不要为您的水桶设置任何位置)。
最后将 SCNTorus 定位在 float3(0.0,0.20,0.0) 这样的地方,这是 20 厘米。在锚的中心上方
我在代码中解决了我的问题,方法是在 SceneKit 编辑器中删除桶的基部,然后使用 SCNCylinder 作为场景的新节点通过代码向其添加另一个基部。然后我使用新节点通过接触位掩码与我的球接触。