当两个对象在 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 作为场景的新节点通过代码向其添加另一个基部。然后我使用新节点通过接触位掩码与我的球接触。