I am making a game right now in SpriteKit, and the purpose of the game is that there is a gunner who shoots bullets, and the goal is to shoot the balls. Right now, I have added basic collision detection so that when a bullet hits a ball, the ball will be moved to the right from the impact of the bullet. I want to make it so when the bullet touches the ball, the ball explodes. I know that I will need a .atlas file for the animation of the bullet, but how can I make it so the game recognizes when the bullet touches the ball, then sets off the animation.
Trouble with Collision Detection
Hi ExpertExp,
Implement your GameScene as the SKPhysicsContactDelegate
Add your categoryBitMasks and contactTestBitMasks to your ball and bullet physics bodies
ball.physicsBody!.categoryBitMask = Category.Ball.rawValue
ball.physicsBody!.contactTestBitMask = Category.Bullet.rawValue
bullet.physicsBody!.categoryBitMask = Category.Bullet.rawValue
bullet.physicsBody!.contactTestBitMask = Category.Ball.raw
Implement didBeginContact: in your GameScene
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case Category.Ball.rawValue | Category.Bullet.rawValue:
if let ball = contact.bodyA.node as? Ball, bullet = contact.bodyB.node as? Bullet {
// Your blow up the ball code
ball.blowUp()
bullet.removeFromParent()
playLoudExplosionSoundFX()
} else if bullet = contact.bodyA.node as? Bullet, ball = contact.bodyB.node as? Ball {
ball.blowUp()
bullet.removeFromParent()
playLoudExplosionSoundFX()
}
default: break
}
}
There is probably a cleaner way to implement your blow up the ball code in didBeginContact:, without repeating code.
I hope this helps
Jim
Thank you for your help Jim. It was what I was looking for: however, when I implemented it into my code, I recieved a couple of errors. I debugged some of them, but most of them were past my level.
Can someone please look over this code and tell me where I have made mistakes.
Thank you,
Patrick
This is my code -
import UIKit
import SpriteKit
class ShooterScene: SKScene {
var score = 0
var enemyCount = 50
var shooterAnimation = [SKTexture]()
override func didMoveToView(view: SKView) {
/Changes Gravity
self.physicsWorld.gravity = CGVectorMake(0, -1)
*/
self.initShooterScene()
}
func initShooterScene(){
let shooterAtlas = SKTextureAtlas(named: "shooter")
for index in 1...shooterAtlas.textureNames.count {
let imgName = String(format: "shooter%01d", index)
shooterAnimation += [shooterAtlas.textureNamed(imgName)]
}
let dropBalls = SKAction.sequence([SKAction.runBlock({
self.createBallNode()}),
SKAction.waitForDuration(0.25)])
self.runAction(SKAction.repeatAction(dropBalls, count: enemyCount), completion: nil)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let shooterNode = self.childNodeWithName("shooterNode")
if(shooterNode != nil) {
let animation = SKAction.animateWithTextures(shooterAnimation, timePerFrame: 0.075)
let shootBullet = SKAction.runBlock({
let BulletNode = self.createBulletNode()
self.addChild(BulletNode)
BulletNode.physicsBody?.applyImpulse(CGVectorMake(300, 0))
})
let sequence = SKAction.sequence([animation, shootBullet])
shooterNode?.runAction(sequence)
}
}
func createBulletNode() -> SKSpriteNode {
let shooterNode = self.childNodeWithName("shooterNode")
let shooterPosition = shooterNode?.position
let shooterWidth = shooterNode?.frame.size.width
let Bullet = SKSpriteNode(imageNamed: "bullet.png")
Bullet.position = CGPointMake(shooterPosition!.x + shooterWidth!/2, shooterPosition!.y + 60)
Bullet.name = "BulletNode"
Bullet.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet.frame.size)
Bullet.physicsBody?.usesPreciseCollisionDetection = true
Bullet.physicsBody!.categoryBitMask = Category.Bullet.rawValue ! Error - 'Category.Type' does not have a member named 'Bullet'
Bullet.physicsBody!.contactTestBitMask = Category.Ball.rawValue ! Error - 'Category.Type' does not have a member named 'Ball'
return Bullet
}
func createBallNode() {
let Ball = SKSpriteNode(imageNamed: "starball.png")
Ball.position = CGPointMake(randomNumber(self.size.width) + 250, self.size.height)
Ball.name = "BallNode"
Ball.physicsBody = SKPhysicsBody(circleOfRadius: Ball.size.width/2)
Ball.physicsBody?.usesPreciseCollisionDetection = true
Ball.physicsBody!.categoryBitMask = Category.Ball.rawValue ! Error - 'Category.Type' does not have a member named 'Ball' Ball.physicsBody!.contactTestBitMask = Category.Bullet.rawValue ! Error - 'Category.Type' does not have a member named 'Bullet'
self.addChild(Ball)
}
func randomNumber(maximum: CGFloat) -> CGFloat {
let maxInt = UInt32(maximum)
let result = arc4random_uniform(maxInt)
return CGFloat(result)
}
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case Category.Ball.rawValue | Category.Bullet.rawValue: !Error - 'Category.Type' does not have a member named 'Bullet' if let Ball = contact.bodyA.node as? Ball, Bullet = contact.bodyB.node as? Bullet { !Error - Use of undeclared variable 'Ball'
/
Ball.blowUp()
Bullet.removeFromParent()
playLoudExplosionSoundFX()
} else if Bullet = contact.bodyA.node as? Bullet && Ball = contact.bodyB.node as? Ball {
Ball.blowUp()
Bullet.removeFromParent()
playLoudExplosionSoundFX()
}
default: break
}
}
}
Hi Patrick,
Did you implement your Categories as an Enumerator first?
At the top of your GameScene, above your class decleration. Add a Category Enumerator
enum Category: UInt32 {
case Ball = 1
case Bullet = 2
case AnotherEntity = 4 // additional entities are added by doubling the previous value.
}
These are the values of the categoryBitMask.
The way your Contact delegate calculates (sort of), or, the way I think of it is this...
Your Ball has a value of 1 and your Bullet has a value of 2...
Set your Bullet to have the category value of
categoryBitMask = Category.Bullet.rawValue
categoryBitMask = 2
Set your Bullet to test for contact
contactTestBitMask = Category.Ball.rawValue
contactTestBitMask = 1
your PhysicsContactDelegate will register the contacts as integers. So your switch statement is
case: Category.Bullet.rawValue | Category.Ball.rawValue
2 + 1 = 3
When your Contact delegate registers a contact of 3 your Bullet and Ball have contacted each other.
If you add one of your categories to the collisionBitMask, the entities will collide.
I hope this helps.
Cheers
Jim
Thank you Jim for helping me. I really appreciate the work you put in to help me. Your code was very helpful and I learned a lot from it. Thank you very much.
-Patrick