Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags
Strange use of "whether ... than ..." in official text
Could a dragon use its wings to swim?
Car headlights in a world without electricity
How to avoid supervisors with prejudiced views?
Is the offspring between a demon and a celestial possible? If so what is it called and is it in a book somewhere?
Can I board the first leg of the flight without having final country's PR card?
subequations: How to continue numbering within subequation?
How do you define an element with an ID attribute using LWC?
Relevant Part for a Badminton Serve
What CSS properties can the br tag have?
What are the unusually-enlarged wing sections on this P-38 Lightning?
Why did early computer designers eschew integers?
Is it a bad idea to plug the other end of ESD strap to wall ground?
Magento 1.9 observer on only new product added ( no updated product )
Airplane gently rocking its wings during whole flight
It is correct to match light sources with the same color temperature?
Cardinal characteristics of amorphous sets
DISTINCT column combination with permutations
Single word request for not liking to be touched
Ising model simulation
Players Circumventing the limitations of Wish
Why was Sir Cadogan fired?
From jafe to El-Guest
Can I send my tax returns from abroad to IRS Austin
Swift 4 - func physicsWorld not invoked on collision?
The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags
I'm trying to make a collision effect when my jet is hit by a bullet or touched by the player directly. The animation and the touch explosion works.
But when the bullet hits the jet planes it's not exploding.
Note: Already tried adding
sceneView.scene.physicsWorld.contactDelegate = self
in viewDidLoad()
//
// ViewController.swift
//
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate, SCNPhysicsContactDelegate
// MARK: - Variables
@IBOutlet var sceneView: ARSCNView!
var visNode: SCNNode!
var mainContainer: SCNNode!
var gameHasStarted = false
var foundSurface = false
var gamePos = SCNVector3Make(0.0, 0.0, 0.0)
var scoreLbl: UILabel!
var player: AVAudioPlayer!
var score = 0
didSet
scoreLbl.text = "(score)"
// MARK: - View Controller Handling
override func viewDidLoad()
super.viewDidLoad()
// Scene
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/scene.scn")!
sceneView.scene = scene
sceneView.scene.physicsWorld.contactDelegate = self
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
//let configuration = ARWorldTrackingConfiguration()
//sceneView.session.run(configuration)
self.configureSession()
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
sceneView.session.pause()
// MARK: - Custom functions
func randomPos() -> SCNVector3
let randX = (Float(arc4random_uniform(200)) / 100.0) - 1.0
let randY = (Float(arc4random_uniform(100)) / 100.0) + 1.5
return SCNVector3Make(randX, randY, -5.0)
@objc func addPlane()
let plane = sceneView.scene.rootNode.childNode(withName: "plane", recursively: false)?.copy() as! SCNNode
plane.position = randomPos()
plane.isHidden = false
mainContainer.addChildNode(plane)
let randSpeed = Float(arc4random_uniform(3) + 1)
//original - let randSpeed = Float(arc4random_uniform(3) + 3)
let planeAnimation = SCNAction.sequence([SCNAction.wait(duration: 10.0), SCNAction.fadeOut(duration: 1.0), SCNAction.removeFromParentNode()])
plane.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
plane.physicsBody?.isAffectedByGravity = false
plane.physicsBody?.categoryBitMask = CollisionCategory.ship.rawValue
plane.physicsBody?.contactTestBitMask = CollisionCategory.bullets.rawValue
plane.physicsBody?.applyForce(SCNVector3Make(0.0, 0.0, randSpeed), asImpulse: true)
plane.runAction(planeAnimation)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(addPlane), userInfo: nil, repeats: false)
func getUserVector() -> (SCNVector3, SCNVector3) // (direction, position)
if let frame = self.sceneView.session.currentFrame
let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
let dir = SCNVector3(-30 * mat.m31, -30 * mat.m32, -10 * mat.m33) // orientation of camera in world space
let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space
return (dir, pos)
return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2))
// MARK: - Scene Handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
if gameHasStarted
let bulletsNode = Bullet()
let (direction, position) = self.getUserVector()
bulletsNode.position = position // SceneKit/AR coordinates are in meters
let bulletDirection = direction
bulletsNode.physicsBody?.velocity = SCNVector3Make(0.0, 0.0, -5)
bulletsNode.physicsBody?.applyForce(bulletDirection, asImpulse: true)
sceneView.scene.rootNode.addChildNode(bulletsNode)
guard let touch = touches.first else return
let touchLocation = touch.location(in: view)
guard let hitTestTouch = sceneView.hitTest(touchLocation, options: nil).first else return
let touchedNode = hitTestTouch.node
guard touchedNode.name == "plane" else return
touchedNode.physicsBody?.isAffectedByGravity = true
touchedNode.physicsBody?.applyTorque(SCNVector4Make(0.0, 0.3, 1.0, 1.0), asImpulse: true)
score += 1
let explosion = SCNParticleSystem(named: "Explosion.scnp", inDirectory: nil)!
touchedNode.addParticleSystem(explosion)
else
guard foundSurface else return
gameHasStarted = true
visNode.removeFromParentNode()
// Score Lbl
scoreLbl = UILabel(frame: CGRect(x: 0.0, y: view.frame.height * 0.05, width: view.frame.width, height: view.frame.height * 0.1))
scoreLbl.textColor = .yellow
scoreLbl.font = UIFont(name: "Arial", size: view.frame.width * 0.1)
scoreLbl.text = "0"
scoreLbl.textAlignment = .center
view.addSubview(scoreLbl)
// Main Container
mainContainer = sceneView.scene.rootNode.childNode(withName: "mainContainer", recursively: false)!
mainContainer.isHidden = false
mainContainer.position = gamePos
// Lighting (Ambient)
let ambientLight = SCNLight()
ambientLight.type = .ambient
ambientLight.color = UIColor.white
ambientLight.intensity = 300.0
let ambientLightNode = SCNNode()
ambientLightNode.light = ambientLight
ambientLightNode.position.y = 2.0
mainContainer.addChildNode(ambientLightNode)
// Lighting (Omnidirectional)
let omniLight = SCNLight()
omniLight.type = .omni
omniLight.color = UIColor.white
omniLight.intensity = 1000.0
let omniLightNode = SCNNode()
omniLightNode.light = omniLight
omniLightNode.position.y = 3.0
mainContainer.addChildNode(omniLightNode)
addPlane()
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
guard !gameHasStarted else return
guard let hitTest = sceneView.hitTest(CGPoint(x: view.frame.midX, y: view.frame.midY), types: [.existingPlane, .featurePoint, .estimatedHorizontalPlane]).last else return
let transform = SCNMatrix4(hitTest.worldTransform)
gamePos = SCNVector3Make(transform.m41, transform.m42, transform.m43)
if visNode == nil
let visPlane = SCNPlane(width: 0.3, height: 0.3)
visPlane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tracker")
visNode = SCNNode(geometry: visPlane)
visNode.eulerAngles.x = .pi * -0.5
sceneView.scene.rootNode.addChildNode(visNode)
visNode.position = gamePos
foundSurface = true
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
func removeNodeWithAnimation(_ node: SCNNode, explosion: Bool)
// Play collision sound for all collisions (bullet-bullet, etc.)
self.playSoundEffect(ofType: .collision)
if explosion
// Play explosion sound for bullet-ship collisions
self.playSoundEffect(ofType: .explosion)
let particleSystem = SCNParticleSystem(named: "Explosion", inDirectory: nil)
let systemNode = SCNNode()
systemNode.addParticleSystem(particleSystem!)
// place explosion where node is
systemNode.position = node.position
sceneView.scene.rootNode.addChildNode(systemNode)
// remove node
node.removeFromParentNode()
// MARK: - Sound Effects
func playSoundEffect(ofType effect: SoundEffect)
// Async to avoid substantial cost to graphics processing (may result in sound effect delay however)
DispatchQueue.main.async
do
if let effectURL = Bundle.main.url(forResource: effect.rawValue, withExtension: "mp3")
self.player = try AVAudioPlayer(contentsOf: effectURL)
self.player.play()
catch let error as NSError
print(error.description)
func configureSession()
if ARWorldTrackingConfiguration.isSupported // checks if user's device supports the more precise ARWorldTrackingSessionConfiguration
// equivalent to `if utsname().hasAtLeastA9()`
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = ARWorldTrackingConfiguration.PlaneDetection.horizontal
// Run the view's session
sceneView.session.run(configuration)
else
// slightly less immersive AR experience due to lower end processor
let configuration = AROrientationTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
struct CollisionCategory: OptionSet
let rawValue: Int
static let bullets = CollisionCategory(rawValue: 1 << 0) // 00...01
static let ship = CollisionCategory(rawValue: 1 << 1) // 00..10
extension utsname
func hasAtLeastA9() -> Bool // checks if device has at least A9 chip for configuration
var systemInfo = self
uname(&systemInfo)
let str = withUnsafePointer(to: &systemInfo.machine.0) ptr in
return String(cString: ptr)
switch str
case "iPhone8,1", "iPhone8,2", "iPhone8,4", "iPhone9,1", "iPhone9,2", "iPhone9,3", "iPhone9,4": // iphone with at least A9 processor
return true
case "iPad6,7", "iPad6,8", "iPad6,3", "iPad6,4", "iPad6,11", "iPad6,12": // ipad with at least A9 processor
return true
default:
return false
enum SoundEffect: String
case explosion = "explosion"
case collision = "collision"
case torpedo = "torpedo"
Bullet.swift
import UIKit
import SceneKit
// Spheres that are shot at the "ships"
class Bullet: SCNNode
override init ()
super.init()
let sphere = SCNSphere(radius: 0.025)
self.geometry = sphere
let shape = SCNPhysicsShape(geometry: sphere, options: nil)
self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
self.physicsBody?.isAffectedByGravity = false
// see http://texnotes.me/post/5/ for details on collisions and bit masks
self.physicsBody?.categoryBitMask = CollisionCategory.bullets.rawValue
self.physicsBody?.contactTestBitMask = CollisionCategory.ship.rawValue
// add texture
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "bullet_texture")
self.geometry?.materials = [material]
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ios swift xcode swift4
add a comment |
I'm trying to make a collision effect when my jet is hit by a bullet or touched by the player directly. The animation and the touch explosion works.
But when the bullet hits the jet planes it's not exploding.
Note: Already tried adding
sceneView.scene.physicsWorld.contactDelegate = self
in viewDidLoad()
//
// ViewController.swift
//
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate, SCNPhysicsContactDelegate
// MARK: - Variables
@IBOutlet var sceneView: ARSCNView!
var visNode: SCNNode!
var mainContainer: SCNNode!
var gameHasStarted = false
var foundSurface = false
var gamePos = SCNVector3Make(0.0, 0.0, 0.0)
var scoreLbl: UILabel!
var player: AVAudioPlayer!
var score = 0
didSet
scoreLbl.text = "(score)"
// MARK: - View Controller Handling
override func viewDidLoad()
super.viewDidLoad()
// Scene
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/scene.scn")!
sceneView.scene = scene
sceneView.scene.physicsWorld.contactDelegate = self
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
//let configuration = ARWorldTrackingConfiguration()
//sceneView.session.run(configuration)
self.configureSession()
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
sceneView.session.pause()
// MARK: - Custom functions
func randomPos() -> SCNVector3
let randX = (Float(arc4random_uniform(200)) / 100.0) - 1.0
let randY = (Float(arc4random_uniform(100)) / 100.0) + 1.5
return SCNVector3Make(randX, randY, -5.0)
@objc func addPlane()
let plane = sceneView.scene.rootNode.childNode(withName: "plane", recursively: false)?.copy() as! SCNNode
plane.position = randomPos()
plane.isHidden = false
mainContainer.addChildNode(plane)
let randSpeed = Float(arc4random_uniform(3) + 1)
//original - let randSpeed = Float(arc4random_uniform(3) + 3)
let planeAnimation = SCNAction.sequence([SCNAction.wait(duration: 10.0), SCNAction.fadeOut(duration: 1.0), SCNAction.removeFromParentNode()])
plane.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
plane.physicsBody?.isAffectedByGravity = false
plane.physicsBody?.categoryBitMask = CollisionCategory.ship.rawValue
plane.physicsBody?.contactTestBitMask = CollisionCategory.bullets.rawValue
plane.physicsBody?.applyForce(SCNVector3Make(0.0, 0.0, randSpeed), asImpulse: true)
plane.runAction(planeAnimation)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(addPlane), userInfo: nil, repeats: false)
func getUserVector() -> (SCNVector3, SCNVector3) // (direction, position)
if let frame = self.sceneView.session.currentFrame
let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
let dir = SCNVector3(-30 * mat.m31, -30 * mat.m32, -10 * mat.m33) // orientation of camera in world space
let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space
return (dir, pos)
return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2))
// MARK: - Scene Handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
if gameHasStarted
let bulletsNode = Bullet()
let (direction, position) = self.getUserVector()
bulletsNode.position = position // SceneKit/AR coordinates are in meters
let bulletDirection = direction
bulletsNode.physicsBody?.velocity = SCNVector3Make(0.0, 0.0, -5)
bulletsNode.physicsBody?.applyForce(bulletDirection, asImpulse: true)
sceneView.scene.rootNode.addChildNode(bulletsNode)
guard let touch = touches.first else return
let touchLocation = touch.location(in: view)
guard let hitTestTouch = sceneView.hitTest(touchLocation, options: nil).first else return
let touchedNode = hitTestTouch.node
guard touchedNode.name == "plane" else return
touchedNode.physicsBody?.isAffectedByGravity = true
touchedNode.physicsBody?.applyTorque(SCNVector4Make(0.0, 0.3, 1.0, 1.0), asImpulse: true)
score += 1
let explosion = SCNParticleSystem(named: "Explosion.scnp", inDirectory: nil)!
touchedNode.addParticleSystem(explosion)
else
guard foundSurface else return
gameHasStarted = true
visNode.removeFromParentNode()
// Score Lbl
scoreLbl = UILabel(frame: CGRect(x: 0.0, y: view.frame.height * 0.05, width: view.frame.width, height: view.frame.height * 0.1))
scoreLbl.textColor = .yellow
scoreLbl.font = UIFont(name: "Arial", size: view.frame.width * 0.1)
scoreLbl.text = "0"
scoreLbl.textAlignment = .center
view.addSubview(scoreLbl)
// Main Container
mainContainer = sceneView.scene.rootNode.childNode(withName: "mainContainer", recursively: false)!
mainContainer.isHidden = false
mainContainer.position = gamePos
// Lighting (Ambient)
let ambientLight = SCNLight()
ambientLight.type = .ambient
ambientLight.color = UIColor.white
ambientLight.intensity = 300.0
let ambientLightNode = SCNNode()
ambientLightNode.light = ambientLight
ambientLightNode.position.y = 2.0
mainContainer.addChildNode(ambientLightNode)
// Lighting (Omnidirectional)
let omniLight = SCNLight()
omniLight.type = .omni
omniLight.color = UIColor.white
omniLight.intensity = 1000.0
let omniLightNode = SCNNode()
omniLightNode.light = omniLight
omniLightNode.position.y = 3.0
mainContainer.addChildNode(omniLightNode)
addPlane()
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
guard !gameHasStarted else return
guard let hitTest = sceneView.hitTest(CGPoint(x: view.frame.midX, y: view.frame.midY), types: [.existingPlane, .featurePoint, .estimatedHorizontalPlane]).last else return
let transform = SCNMatrix4(hitTest.worldTransform)
gamePos = SCNVector3Make(transform.m41, transform.m42, transform.m43)
if visNode == nil
let visPlane = SCNPlane(width: 0.3, height: 0.3)
visPlane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tracker")
visNode = SCNNode(geometry: visPlane)
visNode.eulerAngles.x = .pi * -0.5
sceneView.scene.rootNode.addChildNode(visNode)
visNode.position = gamePos
foundSurface = true
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
func removeNodeWithAnimation(_ node: SCNNode, explosion: Bool)
// Play collision sound for all collisions (bullet-bullet, etc.)
self.playSoundEffect(ofType: .collision)
if explosion
// Play explosion sound for bullet-ship collisions
self.playSoundEffect(ofType: .explosion)
let particleSystem = SCNParticleSystem(named: "Explosion", inDirectory: nil)
let systemNode = SCNNode()
systemNode.addParticleSystem(particleSystem!)
// place explosion where node is
systemNode.position = node.position
sceneView.scene.rootNode.addChildNode(systemNode)
// remove node
node.removeFromParentNode()
// MARK: - Sound Effects
func playSoundEffect(ofType effect: SoundEffect)
// Async to avoid substantial cost to graphics processing (may result in sound effect delay however)
DispatchQueue.main.async
do
if let effectURL = Bundle.main.url(forResource: effect.rawValue, withExtension: "mp3")
self.player = try AVAudioPlayer(contentsOf: effectURL)
self.player.play()
catch let error as NSError
print(error.description)
func configureSession()
if ARWorldTrackingConfiguration.isSupported // checks if user's device supports the more precise ARWorldTrackingSessionConfiguration
// equivalent to `if utsname().hasAtLeastA9()`
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = ARWorldTrackingConfiguration.PlaneDetection.horizontal
// Run the view's session
sceneView.session.run(configuration)
else
// slightly less immersive AR experience due to lower end processor
let configuration = AROrientationTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
struct CollisionCategory: OptionSet
let rawValue: Int
static let bullets = CollisionCategory(rawValue: 1 << 0) // 00...01
static let ship = CollisionCategory(rawValue: 1 << 1) // 00..10
extension utsname
func hasAtLeastA9() -> Bool // checks if device has at least A9 chip for configuration
var systemInfo = self
uname(&systemInfo)
let str = withUnsafePointer(to: &systemInfo.machine.0) ptr in
return String(cString: ptr)
switch str
case "iPhone8,1", "iPhone8,2", "iPhone8,4", "iPhone9,1", "iPhone9,2", "iPhone9,3", "iPhone9,4": // iphone with at least A9 processor
return true
case "iPad6,7", "iPad6,8", "iPad6,3", "iPad6,4", "iPad6,11", "iPad6,12": // ipad with at least A9 processor
return true
default:
return false
enum SoundEffect: String
case explosion = "explosion"
case collision = "collision"
case torpedo = "torpedo"
Bullet.swift
import UIKit
import SceneKit
// Spheres that are shot at the "ships"
class Bullet: SCNNode
override init ()
super.init()
let sphere = SCNSphere(radius: 0.025)
self.geometry = sphere
let shape = SCNPhysicsShape(geometry: sphere, options: nil)
self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
self.physicsBody?.isAffectedByGravity = false
// see http://texnotes.me/post/5/ for details on collisions and bit masks
self.physicsBody?.categoryBitMask = CollisionCategory.bullets.rawValue
self.physicsBody?.contactTestBitMask = CollisionCategory.ship.rawValue
// add texture
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "bullet_texture")
self.geometry?.materials = [material]
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ios swift xcode swift4
add a comment |
I'm trying to make a collision effect when my jet is hit by a bullet or touched by the player directly. The animation and the touch explosion works.
But when the bullet hits the jet planes it's not exploding.
Note: Already tried adding
sceneView.scene.physicsWorld.contactDelegate = self
in viewDidLoad()
//
// ViewController.swift
//
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate, SCNPhysicsContactDelegate
// MARK: - Variables
@IBOutlet var sceneView: ARSCNView!
var visNode: SCNNode!
var mainContainer: SCNNode!
var gameHasStarted = false
var foundSurface = false
var gamePos = SCNVector3Make(0.0, 0.0, 0.0)
var scoreLbl: UILabel!
var player: AVAudioPlayer!
var score = 0
didSet
scoreLbl.text = "(score)"
// MARK: - View Controller Handling
override func viewDidLoad()
super.viewDidLoad()
// Scene
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/scene.scn")!
sceneView.scene = scene
sceneView.scene.physicsWorld.contactDelegate = self
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
//let configuration = ARWorldTrackingConfiguration()
//sceneView.session.run(configuration)
self.configureSession()
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
sceneView.session.pause()
// MARK: - Custom functions
func randomPos() -> SCNVector3
let randX = (Float(arc4random_uniform(200)) / 100.0) - 1.0
let randY = (Float(arc4random_uniform(100)) / 100.0) + 1.5
return SCNVector3Make(randX, randY, -5.0)
@objc func addPlane()
let plane = sceneView.scene.rootNode.childNode(withName: "plane", recursively: false)?.copy() as! SCNNode
plane.position = randomPos()
plane.isHidden = false
mainContainer.addChildNode(plane)
let randSpeed = Float(arc4random_uniform(3) + 1)
//original - let randSpeed = Float(arc4random_uniform(3) + 3)
let planeAnimation = SCNAction.sequence([SCNAction.wait(duration: 10.0), SCNAction.fadeOut(duration: 1.0), SCNAction.removeFromParentNode()])
plane.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
plane.physicsBody?.isAffectedByGravity = false
plane.physicsBody?.categoryBitMask = CollisionCategory.ship.rawValue
plane.physicsBody?.contactTestBitMask = CollisionCategory.bullets.rawValue
plane.physicsBody?.applyForce(SCNVector3Make(0.0, 0.0, randSpeed), asImpulse: true)
plane.runAction(planeAnimation)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(addPlane), userInfo: nil, repeats: false)
func getUserVector() -> (SCNVector3, SCNVector3) // (direction, position)
if let frame = self.sceneView.session.currentFrame
let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
let dir = SCNVector3(-30 * mat.m31, -30 * mat.m32, -10 * mat.m33) // orientation of camera in world space
let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space
return (dir, pos)
return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2))
// MARK: - Scene Handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
if gameHasStarted
let bulletsNode = Bullet()
let (direction, position) = self.getUserVector()
bulletsNode.position = position // SceneKit/AR coordinates are in meters
let bulletDirection = direction
bulletsNode.physicsBody?.velocity = SCNVector3Make(0.0, 0.0, -5)
bulletsNode.physicsBody?.applyForce(bulletDirection, asImpulse: true)
sceneView.scene.rootNode.addChildNode(bulletsNode)
guard let touch = touches.first else return
let touchLocation = touch.location(in: view)
guard let hitTestTouch = sceneView.hitTest(touchLocation, options: nil).first else return
let touchedNode = hitTestTouch.node
guard touchedNode.name == "plane" else return
touchedNode.physicsBody?.isAffectedByGravity = true
touchedNode.physicsBody?.applyTorque(SCNVector4Make(0.0, 0.3, 1.0, 1.0), asImpulse: true)
score += 1
let explosion = SCNParticleSystem(named: "Explosion.scnp", inDirectory: nil)!
touchedNode.addParticleSystem(explosion)
else
guard foundSurface else return
gameHasStarted = true
visNode.removeFromParentNode()
// Score Lbl
scoreLbl = UILabel(frame: CGRect(x: 0.0, y: view.frame.height * 0.05, width: view.frame.width, height: view.frame.height * 0.1))
scoreLbl.textColor = .yellow
scoreLbl.font = UIFont(name: "Arial", size: view.frame.width * 0.1)
scoreLbl.text = "0"
scoreLbl.textAlignment = .center
view.addSubview(scoreLbl)
// Main Container
mainContainer = sceneView.scene.rootNode.childNode(withName: "mainContainer", recursively: false)!
mainContainer.isHidden = false
mainContainer.position = gamePos
// Lighting (Ambient)
let ambientLight = SCNLight()
ambientLight.type = .ambient
ambientLight.color = UIColor.white
ambientLight.intensity = 300.0
let ambientLightNode = SCNNode()
ambientLightNode.light = ambientLight
ambientLightNode.position.y = 2.0
mainContainer.addChildNode(ambientLightNode)
// Lighting (Omnidirectional)
let omniLight = SCNLight()
omniLight.type = .omni
omniLight.color = UIColor.white
omniLight.intensity = 1000.0
let omniLightNode = SCNNode()
omniLightNode.light = omniLight
omniLightNode.position.y = 3.0
mainContainer.addChildNode(omniLightNode)
addPlane()
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
guard !gameHasStarted else return
guard let hitTest = sceneView.hitTest(CGPoint(x: view.frame.midX, y: view.frame.midY), types: [.existingPlane, .featurePoint, .estimatedHorizontalPlane]).last else return
let transform = SCNMatrix4(hitTest.worldTransform)
gamePos = SCNVector3Make(transform.m41, transform.m42, transform.m43)
if visNode == nil
let visPlane = SCNPlane(width: 0.3, height: 0.3)
visPlane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tracker")
visNode = SCNNode(geometry: visPlane)
visNode.eulerAngles.x = .pi * -0.5
sceneView.scene.rootNode.addChildNode(visNode)
visNode.position = gamePos
foundSurface = true
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
func removeNodeWithAnimation(_ node: SCNNode, explosion: Bool)
// Play collision sound for all collisions (bullet-bullet, etc.)
self.playSoundEffect(ofType: .collision)
if explosion
// Play explosion sound for bullet-ship collisions
self.playSoundEffect(ofType: .explosion)
let particleSystem = SCNParticleSystem(named: "Explosion", inDirectory: nil)
let systemNode = SCNNode()
systemNode.addParticleSystem(particleSystem!)
// place explosion where node is
systemNode.position = node.position
sceneView.scene.rootNode.addChildNode(systemNode)
// remove node
node.removeFromParentNode()
// MARK: - Sound Effects
func playSoundEffect(ofType effect: SoundEffect)
// Async to avoid substantial cost to graphics processing (may result in sound effect delay however)
DispatchQueue.main.async
do
if let effectURL = Bundle.main.url(forResource: effect.rawValue, withExtension: "mp3")
self.player = try AVAudioPlayer(contentsOf: effectURL)
self.player.play()
catch let error as NSError
print(error.description)
func configureSession()
if ARWorldTrackingConfiguration.isSupported // checks if user's device supports the more precise ARWorldTrackingSessionConfiguration
// equivalent to `if utsname().hasAtLeastA9()`
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = ARWorldTrackingConfiguration.PlaneDetection.horizontal
// Run the view's session
sceneView.session.run(configuration)
else
// slightly less immersive AR experience due to lower end processor
let configuration = AROrientationTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
struct CollisionCategory: OptionSet
let rawValue: Int
static let bullets = CollisionCategory(rawValue: 1 << 0) // 00...01
static let ship = CollisionCategory(rawValue: 1 << 1) // 00..10
extension utsname
func hasAtLeastA9() -> Bool // checks if device has at least A9 chip for configuration
var systemInfo = self
uname(&systemInfo)
let str = withUnsafePointer(to: &systemInfo.machine.0) ptr in
return String(cString: ptr)
switch str
case "iPhone8,1", "iPhone8,2", "iPhone8,4", "iPhone9,1", "iPhone9,2", "iPhone9,3", "iPhone9,4": // iphone with at least A9 processor
return true
case "iPad6,7", "iPad6,8", "iPad6,3", "iPad6,4", "iPad6,11", "iPad6,12": // ipad with at least A9 processor
return true
default:
return false
enum SoundEffect: String
case explosion = "explosion"
case collision = "collision"
case torpedo = "torpedo"
Bullet.swift
import UIKit
import SceneKit
// Spheres that are shot at the "ships"
class Bullet: SCNNode
override init ()
super.init()
let sphere = SCNSphere(radius: 0.025)
self.geometry = sphere
let shape = SCNPhysicsShape(geometry: sphere, options: nil)
self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
self.physicsBody?.isAffectedByGravity = false
// see http://texnotes.me/post/5/ for details on collisions and bit masks
self.physicsBody?.categoryBitMask = CollisionCategory.bullets.rawValue
self.physicsBody?.contactTestBitMask = CollisionCategory.ship.rawValue
// add texture
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "bullet_texture")
self.geometry?.materials = [material]
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ios swift xcode swift4
I'm trying to make a collision effect when my jet is hit by a bullet or touched by the player directly. The animation and the touch explosion works.
But when the bullet hits the jet planes it's not exploding.
Note: Already tried adding
sceneView.scene.physicsWorld.contactDelegate = self
in viewDidLoad()
//
// ViewController.swift
//
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate, SCNPhysicsContactDelegate
// MARK: - Variables
@IBOutlet var sceneView: ARSCNView!
var visNode: SCNNode!
var mainContainer: SCNNode!
var gameHasStarted = false
var foundSurface = false
var gamePos = SCNVector3Make(0.0, 0.0, 0.0)
var scoreLbl: UILabel!
var player: AVAudioPlayer!
var score = 0
didSet
scoreLbl.text = "(score)"
// MARK: - View Controller Handling
override func viewDidLoad()
super.viewDidLoad()
// Scene
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/scene.scn")!
sceneView.scene = scene
sceneView.scene.physicsWorld.contactDelegate = self
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
//let configuration = ARWorldTrackingConfiguration()
//sceneView.session.run(configuration)
self.configureSession()
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
sceneView.session.pause()
// MARK: - Custom functions
func randomPos() -> SCNVector3
let randX = (Float(arc4random_uniform(200)) / 100.0) - 1.0
let randY = (Float(arc4random_uniform(100)) / 100.0) + 1.5
return SCNVector3Make(randX, randY, -5.0)
@objc func addPlane()
let plane = sceneView.scene.rootNode.childNode(withName: "plane", recursively: false)?.copy() as! SCNNode
plane.position = randomPos()
plane.isHidden = false
mainContainer.addChildNode(plane)
let randSpeed = Float(arc4random_uniform(3) + 1)
//original - let randSpeed = Float(arc4random_uniform(3) + 3)
let planeAnimation = SCNAction.sequence([SCNAction.wait(duration: 10.0), SCNAction.fadeOut(duration: 1.0), SCNAction.removeFromParentNode()])
plane.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
plane.physicsBody?.isAffectedByGravity = false
plane.physicsBody?.categoryBitMask = CollisionCategory.ship.rawValue
plane.physicsBody?.contactTestBitMask = CollisionCategory.bullets.rawValue
plane.physicsBody?.applyForce(SCNVector3Make(0.0, 0.0, randSpeed), asImpulse: true)
plane.runAction(planeAnimation)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(addPlane), userInfo: nil, repeats: false)
func getUserVector() -> (SCNVector3, SCNVector3) // (direction, position)
if let frame = self.sceneView.session.currentFrame
let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
let dir = SCNVector3(-30 * mat.m31, -30 * mat.m32, -10 * mat.m33) // orientation of camera in world space
let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space
return (dir, pos)
return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2))
// MARK: - Scene Handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
if gameHasStarted
let bulletsNode = Bullet()
let (direction, position) = self.getUserVector()
bulletsNode.position = position // SceneKit/AR coordinates are in meters
let bulletDirection = direction
bulletsNode.physicsBody?.velocity = SCNVector3Make(0.0, 0.0, -5)
bulletsNode.physicsBody?.applyForce(bulletDirection, asImpulse: true)
sceneView.scene.rootNode.addChildNode(bulletsNode)
guard let touch = touches.first else return
let touchLocation = touch.location(in: view)
guard let hitTestTouch = sceneView.hitTest(touchLocation, options: nil).first else return
let touchedNode = hitTestTouch.node
guard touchedNode.name == "plane" else return
touchedNode.physicsBody?.isAffectedByGravity = true
touchedNode.physicsBody?.applyTorque(SCNVector4Make(0.0, 0.3, 1.0, 1.0), asImpulse: true)
score += 1
let explosion = SCNParticleSystem(named: "Explosion.scnp", inDirectory: nil)!
touchedNode.addParticleSystem(explosion)
else
guard foundSurface else return
gameHasStarted = true
visNode.removeFromParentNode()
// Score Lbl
scoreLbl = UILabel(frame: CGRect(x: 0.0, y: view.frame.height * 0.05, width: view.frame.width, height: view.frame.height * 0.1))
scoreLbl.textColor = .yellow
scoreLbl.font = UIFont(name: "Arial", size: view.frame.width * 0.1)
scoreLbl.text = "0"
scoreLbl.textAlignment = .center
view.addSubview(scoreLbl)
// Main Container
mainContainer = sceneView.scene.rootNode.childNode(withName: "mainContainer", recursively: false)!
mainContainer.isHidden = false
mainContainer.position = gamePos
// Lighting (Ambient)
let ambientLight = SCNLight()
ambientLight.type = .ambient
ambientLight.color = UIColor.white
ambientLight.intensity = 300.0
let ambientLightNode = SCNNode()
ambientLightNode.light = ambientLight
ambientLightNode.position.y = 2.0
mainContainer.addChildNode(ambientLightNode)
// Lighting (Omnidirectional)
let omniLight = SCNLight()
omniLight.type = .omni
omniLight.color = UIColor.white
omniLight.intensity = 1000.0
let omniLightNode = SCNNode()
omniLightNode.light = omniLight
omniLightNode.position.y = 3.0
mainContainer.addChildNode(omniLightNode)
addPlane()
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
guard !gameHasStarted else return
guard let hitTest = sceneView.hitTest(CGPoint(x: view.frame.midX, y: view.frame.midY), types: [.existingPlane, .featurePoint, .estimatedHorizontalPlane]).last else return
let transform = SCNMatrix4(hitTest.worldTransform)
gamePos = SCNVector3Make(transform.m41, transform.m42, transform.m43)
if visNode == nil
let visPlane = SCNPlane(width: 0.3, height: 0.3)
visPlane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tracker")
visNode = SCNNode(geometry: visPlane)
visNode.eulerAngles.x = .pi * -0.5
sceneView.scene.rootNode.addChildNode(visNode)
visNode.position = gamePos
foundSurface = true
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
func removeNodeWithAnimation(_ node: SCNNode, explosion: Bool)
// Play collision sound for all collisions (bullet-bullet, etc.)
self.playSoundEffect(ofType: .collision)
if explosion
// Play explosion sound for bullet-ship collisions
self.playSoundEffect(ofType: .explosion)
let particleSystem = SCNParticleSystem(named: "Explosion", inDirectory: nil)
let systemNode = SCNNode()
systemNode.addParticleSystem(particleSystem!)
// place explosion where node is
systemNode.position = node.position
sceneView.scene.rootNode.addChildNode(systemNode)
// remove node
node.removeFromParentNode()
// MARK: - Sound Effects
func playSoundEffect(ofType effect: SoundEffect)
// Async to avoid substantial cost to graphics processing (may result in sound effect delay however)
DispatchQueue.main.async
do
if let effectURL = Bundle.main.url(forResource: effect.rawValue, withExtension: "mp3")
self.player = try AVAudioPlayer(contentsOf: effectURL)
self.player.play()
catch let error as NSError
print(error.description)
func configureSession()
if ARWorldTrackingConfiguration.isSupported // checks if user's device supports the more precise ARWorldTrackingSessionConfiguration
// equivalent to `if utsname().hasAtLeastA9()`
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = ARWorldTrackingConfiguration.PlaneDetection.horizontal
// Run the view's session
sceneView.session.run(configuration)
else
// slightly less immersive AR experience due to lower end processor
let configuration = AROrientationTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
struct CollisionCategory: OptionSet
let rawValue: Int
static let bullets = CollisionCategory(rawValue: 1 << 0) // 00...01
static let ship = CollisionCategory(rawValue: 1 << 1) // 00..10
extension utsname
func hasAtLeastA9() -> Bool // checks if device has at least A9 chip for configuration
var systemInfo = self
uname(&systemInfo)
let str = withUnsafePointer(to: &systemInfo.machine.0) ptr in
return String(cString: ptr)
switch str
case "iPhone8,1", "iPhone8,2", "iPhone8,4", "iPhone9,1", "iPhone9,2", "iPhone9,3", "iPhone9,4": // iphone with at least A9 processor
return true
case "iPad6,7", "iPad6,8", "iPad6,3", "iPad6,4", "iPad6,11", "iPad6,12": // ipad with at least A9 processor
return true
default:
return false
enum SoundEffect: String
case explosion = "explosion"
case collision = "collision"
case torpedo = "torpedo"
Bullet.swift
import UIKit
import SceneKit
// Spheres that are shot at the "ships"
class Bullet: SCNNode
override init ()
super.init()
let sphere = SCNSphere(radius: 0.025)
self.geometry = sphere
let shape = SCNPhysicsShape(geometry: sphere, options: nil)
self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
self.physicsBody?.isAffectedByGravity = false
// see http://texnotes.me/post/5/ for details on collisions and bit masks
self.physicsBody?.categoryBitMask = CollisionCategory.bullets.rawValue
self.physicsBody?.contactTestBitMask = CollisionCategory.ship.rawValue
// add texture
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "bullet_texture")
self.geometry?.materials = [material]
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ios swift xcode swift4
ios swift xcode swift4
asked Mar 21 at 19:07
nanospecknanospeck
1,78422332
1,78422332
add a comment |
add a comment |
0
active
oldest
votes
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55287637%2fswift-4-func-physicsworld-not-invoked-on-collision%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55287637%2fswift-4-func-physicsworld-not-invoked-on-collision%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown