Detect collision between two objects in Swift
Here are a few items missing from your checklist:
- One of the physics bodies must be dynamic
The physics bodies must be moved by a force/impulse or by setting their velocities- The positions of the physics bodies should match their corresponding sprite/shape nodes
For (2), you are moving each bullet by changing its position over time with an SKAction
.
For (3), the position of each bullet starts at (0, 0), while the shape is drawn at the top-center of the scene. Since the physics body is centered at the shape node's position, there is a mismatch between the locations of the shape and the physics body; the bullet's physics body never makes contact with the soldier. To resolve this, try
var bullet = SKShapeNode(rectOfSize: CGSizeMake(10, 40))
bullet.position = CGPointMake(self.size.width/2.0, self.size.height)
Also, the physics body of the soldier is half the radius of its shape.
Comments
-
Don P almost 2 years
Collision detection is pretty simple in Swift - yet on this particular project, body collision is not triggering the
didBeginContact
event as usual.Here is my checklist for two bodies colliding (using soldiers and bullets):
- Add
SKPhysicsContactDelegate
to the class. - set the physicsWorld appropriately, usually:
self.physicsWorld.contactDelegate = self
- Create categories for each group of nodes you will have. E.g.
let bulletCategory = 0x1 << 0
- Create SKNodes for each bullet and soldier.
- Give each bullet and soldier a physics body with a matching shape.
- Set each bullet's categoryBitMask to the
bulletCategory
(soldiers are set to soldierCategory). - Set each bullet's contactBitMask to the
soldierCategory
(soldiers are set to bulletCategory). - Define
didBeginContact()
handler.
Below is my complete code. It is a minimal ~20 line "Hello World" for collision.
If you create a new "Game" and copy paste this into the GameScene.swift, it will run, just no collision events will be fired.
import SpriteKit class GameScene: SKScene, SKPhysicsContactDelegate { var soldier = SKShapeNode(circleOfRadius: 40) let soldierCategory:UInt32 = 0x1 << 0; let bulletCategory:UInt32 = 0x1 << 1; override func didMoveToView(view: SKView) { self.physicsWorld.contactDelegate = self // THE soldier soldier.fillColor = SKColor.redColor() soldier.position = CGPoint(x: CGRectGetMidX(self.frame), y: 40) soldier.physicsBody = SKPhysicsBody(circleOfRadius: 20) soldier.physicsBody!.dynamic = false soldier.physicsBody!.categoryBitMask = soldierCategory soldier.physicsBody!.contactTestBitMask = bulletCategory soldier.physicsBody!.collisionBitMask = 0 // bulletS var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("makeBullet"), userInfo: nil, repeats: true) self.addChild(soldier) } func makeBullet() { var bullet = SKShapeNode(rect: CGRect(x: CGRectGetMidX(self.frame), y: self.frame.height, width: 10, height: 40), cornerRadius: CGFloat(0)) bullet.fillColor = SKColor.redColor() bullet.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 10, height: 40)) bullet.physicsBody?.dynamic = false bullet.physicsBody?.categoryBitMask = bulletCategory bullet.physicsBody?.contactTestBitMask = soldierCategory bullet.physicsBody?.collisionBitMask = soldierCategory var movebullet = SKAction.moveByX(0, y: CGFloat(-400), duration: 1) var movebulletForever = SKAction.repeatActionForever(movebullet) bullet.runAction(movebulletForever) self.addChild(bullet) } func didBeginContact(contact: SKPhysicsContact) { print("CONTACT") } override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { /* Called when a touch begins */ } override func update(currentTime: CFTimeInterval) { /* Called before each frame is rendered */ } }
-
Don P over 9 yearsWait a second. If a physicsBody is not "dynamic" can it not collide with anything?
- Add
-
Don P over 9 yearsThis is awesome, thank you. Is there a way where I can debug or see the positions of the physics bodies? I want to see how they are moving / set differently than the objects, but I can't color them in.
-
0x141E over 9 yearsSet
view.showsPhysics = true
indidMoveToView
or in your view controller. -
Don P over 9 yearsDo both physicsbodies need to be dynamic? In another project, I made a clone of FlappyBirdy, and only one object is dynamic (the bird), while the pipes are not dynamic, and just moved using
SKAction.moveByX()
, the contact event still occurs -
0x141E over 9 yearsI suggest you use a
performSelector
SKAction
to callmakeBullet
instead of using aNSTimer
. Actions pause/resume appropriately when you pause the game (withview.paused = true
) and are removed automatically when you transition scenes. -
Don P over 9 yearsSo right now when a bullet collides with a soldier, it will push the soldier. Do you know how to make them trigger a collision, but continue through eachother?
-
0x141E over 9 yearsYou can set
bullet.physicsBody?.collisionBitMask = 0
to prevent the bullet from colliding with anything, or you can setbullet.physicsBody?.collisionBitMask &= ~soldierCategory
to prevent the bullet from colliding with the soldier only. By default, all bits are set so it will collide with all categories.