我正在创建AR UFO入侵者游戏。我用搅拌器创建了一个UFO,并将.dae文件添加到art.scnassets下的xCode中,并将其转换为.scn。接下来,我使用“触摸开始”功能创建了一个橙色激光节点。我正在尝试检测UFO节点和橙色激光节点何时接触,从而导致得分增加1分。该应用程序的构建和运行是在移动UFO节点并在触摸时也创建橙色激光节点的情况下进行的,但是,它不会检测到接触或碰撞并添加得分。
我尝试了枚举BodyType和struct OptionSet。
import UIKit
import SceneKit
import ARKit
import Foundation
enum BodyType:Int {
case UFO = 1
case orangelaser = 2
case redlaser = 4
}
extension UIColor {
convenience init(rgb: UInt) {
self.init(
red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgb & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
}
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate, SCNPhysicsContactDelegate {
// .........
var gamerunning: Int = 0
var score: Int = 0
var lives: Int = 3
var ammo: Int = 100
var coinCount = 0
var displaylivesammocounter = 0
var rotationtimer: Int = 0
var UFOxposition: Float = 0
var UFOyposition: Float = 0
var UFOzposition: Float = 0
var UFONode: SCNNode!
var orangelaserNode: SCNNode!
var detectedPlanes: [String : SCNNode] = [:]
var translation = matrix_identity_float4x4
var gamePos = SCNVector3Make(0.0, 0.0, 0.0)
// ......
override func viewDidLoad() {
super.viewDidLoad()
// 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(named: "art.scnassets/UFO final with paint.scn")!
// Set the scene to the view
sceneView.scene = scene
sceneView.scene.physicsWorld.contactDelegate = self
createUFO()
// .............
UFONode.isHidden = true
// .........
func createUFO() {
UFONode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
UFONode.physicsBody?.restitution = 0.0
UFONode.physicsBody?.friction = 1.0
// Set the category masks
UFONode.physicsBody?.categoryBitMask = BodyType.UFO.rawValue
// Set the contact masks
UFONode.physicsBody?.contactTestBitMask = BodyType.orangelaser.rawValue
// Set the collission masks
UFONode.physicsBody?.collisionBitMask = BodyType.orangelaser.rawValue
UFONode = sceneView.scene.rootNode.childNode(withName: "UFO", recursively: true)!
sceneView.scene.rootNode.addChildNode(UFONode)
}
// .............
func startGame() {
// .................
gamerunning = 1
UFONode.isHidden = false
// ...................
let UFOrotationTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
if (self.rotationtimer >= 1) {
timer.invalidate()
}
self.UFONode.runAction(SCNAction.repeat(SCNAction.rotateBy(x: 0, y: 1, z: 0, duration: 1), count: 1))
func random(min: CGFloat, max: CGFloat) -> CGFloat {
assert(min < max)
return CGFloat(Float(arc4random()) / 0xFFFFFFFF) * (max - min) + min
}
self.UFOyposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFOxposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFOzposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFONode.runAction(SCNAction.moveBy(x: CGFloat(self.UFOxposition), y: CGFloat(self.UFOyposition), z: CGFloat(self.UFOzposition), duration: 1.0))
})
}
func restartSameGame(){
// ..............
gamerunning = 1
UFONode.isHidden = false
// ................
let UFOrotationTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
if (self.rotationtimer >= 1) {
timer.invalidate()
}
self.UFONode.runAction(SCNAction.repeat(SCNAction.rotateBy(x: 0, y: 1, z: 0, duration: 1), count: 1))
func random(min: CGFloat, max: CGFloat) -> CGFloat {
assert(min < max)
return CGFloat(Float(arc4random()) / 0xFFFFFFFF) * (max - min) + min
}
self.UFOyposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFOxposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFOzposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFONode.runAction(SCNAction.moveBy(x: CGFloat(self.UFOxposition), y: CGFloat(self.UFOyposition), z: CGFloat(self.UFOzposition), duration: 1.0))
})
}
func newGame() {
// ................
score = 0
ammo = 100
lives = 3
// ......................
gamerunning = 1
UFONode.isHidden = false
// ........................
let UFOrotationTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
if (self.rotationtimer >= 1) {
timer.invalidate()
}
self.UFONode.runAction(SCNAction.repeat(SCNAction.rotateBy(x: 0, y: 1, z: 0, duration: 1), count: 1))
func random(min: CGFloat, max: CGFloat) -> CGFloat {
assert(min < max)
return CGFloat(Float(arc4random()) / 0xFFFFFFFF) * (max - min) + min
}
self.UFOyposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFOxposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFOzposition = Float(random(
min: -0.2,
max: 0.2
))
self.UFONode.runAction(SCNAction.moveBy(x: CGFloat(self.UFOxposition), y: CGFloat(self.UFOyposition), z: CGFloat(self.UFOzposition), duration: 1.0))
})
}
@IBAction func PlayGameButton(_ sender: Any) {
startGame()
}
@IBAction func PlayAgainButton(_ sender: Any) {
if interstitial.isReady {
interstitial.present(fromRootViewController: self)
} else {
self.rotationtimer = 0
if (self.ammo > 0 && self.lives > 0) {
self.restartSameGame()
} else {
self.newGame()
}
}
}
func addScore() {
score = score + 1
DisplayScore.text = "\(score)"
DisplayLives.text = "\(lives)"
DisplayAmmo.text = "\(ammo)"
if (lives <= 0) {
gameOver()
}
if (ammo <= 0) {
gameOver()
}
}
func decreaseLives() {
if (lives > 0) {
lives = lives - 1
}
DisplayScore.text = "\(score)"
DisplayLives.text = "\(lives)"
DisplayAmmo.text = "\(ammo)"
if (lives <= 0) {
gameOver()
}
if (ammo <= 0) {
gameOver()
}
}
func decreaseAmmo() {
if (ammo > 0) {
ammo = ammo - 1
}
DisplayScore.text = "\(score)"
DisplayLives.text = "\(lives)"
DisplayAmmo.text = "\(ammo)"
if (lives <= 0) {
gameOver()
}
if (ammo <= 0) {
gameOver()
}
}
// .....................
func gameOver() {
DisplayScore.text = "\(score)"
DisplayLives.text = "\(lives)"
DisplayAmmo.text = "\(ammo)"
UFONode.isHidden = true
gamerunning = 0
MoreLivesAmmoButton.isHidden = false
EarnRewardsButton.isHidden = false
PlayAgainButton.isHidden = false
rotationtimer = 1
// .....................
GameOverLabel.isHidden = false
}
// .............................
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
// 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()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let frame = sceneView.session.currentFrame else { return } //1
let camMatrix = SCNMatrix4(frame.camera.transform)
let direction = SCNVector3Make(-camMatrix.m31 * 5.0, -camMatrix.m32 * 10.0, -camMatrix.m33 * 5.0) //2
let position = SCNVector3Make(camMatrix.m41, camMatrix.m42, camMatrix.m43) //3
if ((ammo > 0) && (lives > 0) && (UFONode.isHidden == false) && (gamerunning == 1)) {
let orangelaser = SCNCapsule(capRadius: 0.25, height: 1.0) //1
orangelaser.firstMaterial?.diffuse.contents = UIColor(red: 245.0 / 255.0, green: 127.0 / 255.0, blue: 44.0 / 255.0, alpha: 1 )
orangelaser.firstMaterial?.emission.contents = UIColor(red: 245.0 / 255.0, green: 127.0 / 255.0, blue: 44.0 / 255.0, alpha: 1 ) //2
orangelaserNode = SCNNode(geometry: orangelaser)
orangelaserNode.scale = SCNVector3(x: 0.009, y: 0.009, z: 0.009)
orangelaserNode.position = position //3
self.orangelaserNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
orangelaserNode.physicsBody?.mass = 1.5; // 1.5kg
orangelaserNode.physicsBody?.restitution = 0.25
orangelaserNode.physicsBody?.friction = 0.75
orangelaserNode.physicsBody?.categoryBitMask = BodyType.orangelaser.rawValue
orangelaserNode.physicsBody?.contactTestBitMask = BodyType.UFO.rawValue
orangelaserNode.physicsBody?.collisionBitMask = BodyType.UFO.rawValue
sceneView.scene.rootNode.addChildNode(orangelaserNode)
orangelaserNode.runAction(SCNAction.sequence([SCNAction.wait(duration: 10.0), SCNAction.removeFromParentNode()])) //5
orangelaserNode.physicsBody?.applyForce(direction, asImpulse: true) //6
decreaseAmmo()
}
}
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
print("contact happened!")
if (contact.nodeA.physicsBody?.categoryBitMask == BodyType.UFO.rawValue && contact.nodeB.physicsBody?.categoryBitMask == BodyType.orangelaser.rawValue ){
print("collision UFO and orangelaser")
addScore()
} else if (contact.nodeB.physicsBody?.categoryBitMask == BodyType.UFO.rawValue && contact.nodeA.physicsBody?.categoryBitMask == BodyType.orangelaser.rawValue ){
print("collision orangelaser and UFO")
addScore()
}
}
// 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
}
}
应用程序生成并运行。我没有看到任何错误消息。但是,当橙色激光节点与UFO接触或碰撞时,什么也没发生。
答案 0 :(得分:0)
要在两个对象之间的ARKit
中实现碰撞检测,请确保已遵循每个步骤:
1。。创建一个包含所有主体类型的枚举:
enum BodyType : Int {
case type1 = 1
case type2 = 2
}
2。。将物理物体和categoryBitMask
应用于所有需要碰撞的节点:
node1.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
node1.physicsBody?.categoryBitMask = BodyType.type1.rawValue
node2.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
node2.physicsBody?.categoryBitMask = BodyType.type2.rawValue
3。。将contactTestBitMask
添加到移动对象中以仅在您的节点上过滤碰撞触发器:
node2.physicsBody?.contactTestBitMask = BodyType.type2.rawValue
4。。符合SCNPhysicsContactDelegate
并实施physicsWorld(_: didBegin:)
:
将此添加到viewDidLoad()
:
self.sceneView.scene.physicsWorld.contactDelegate = self
按照这些步骤,只要您的节点之间发生冲突,就会physicsWorld(_: didBegin:)
委托被调用:
extension ViewController: SCNPhysicsContactDelegate {
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
// Collision happened between contact.nodeA and contact.nodeB
}
}