bell wright bell wright - 2 years ago 146
Swift Question

Swift Sprite disappears when in contact with Bullet

I have this spaceshooter game where it spawns enemy ships randomly. once the player reaches 30, it would spawn a "Boss" Sprite. In the original App, I have it set up to where the boss would appear and only move left and right because I ran into an issue with the sprite disappearing once it collided with the bullet.

I do not have it set up to where if both contacts were made, it would remove the Boss from parent.

I tried to close in on the problem by copying the app and deleting everything besides the player and the boss sprite.

The sprite disappears when physicsWorld.contactDelegate = self is set so it is a collision issue.

Could this be a bug with the xcode?

It is not the image itself because i took out the images and i am using just the placeholder image (big red X) saying that no image of that name was found.

Here is a gif of what it looks like
bullet hitting boss

the left side of the screen shows the bullets being shot from bottom to top
the image coming from right to left is the boss sprite

Here is my gamescene.swift

import SpriteKit
import UIKit

struct PhysicsCatagory {
static let Enemy : UInt32 = 1 //000000000000000000000000000001
static let Bullet : UInt32 = 0b1 //00000000000000000000000000010
static let Player : UInt32 = 4 //00000000000000000000000000100
static let tj: UInt32 = 0b10


class GameScene: SKScene, SKPhysicsContactDelegate {

var Highscore = Int()
var Score = Int()
var BossCounter = Int()
var TJBossHIT = Int()

var Player = SKSpriteNode(imageNamed: "player")
var Enemy = SKSpriteNode(imageNamed: "Enemy.png")
var TJ = SKSpriteNode(imageNamed: "tjBoss9.png")
var Fake = SKSpriteNode(imageNamed: "tjBoss.png")
var ScoreLbl = UILabel()

let HealthBarWidth: CGFloat = 100
let HealthBarHeight: CGFloat = 40
var TJHealth = 20

override func didMoveToView(view: SKView) {

Score = 0

physicsWorld.contactDelegate = self
// self.scene?.backgroundColor = UIColor.darkGrayColor()
self.scene?.size = CGSize(width: 640, height: 1136)
self.scene?.zPosition = 0
Player.position = CGPointMake(self.size.width / 2, self.size.height / 5)
Player.physicsBody = SKPhysicsBody(rectangleOfSize: Player.size)
Player.physicsBody?.affectedByGravity = false
Player.physicsBody?.categoryBitMask = PhysicsCatagory.Player
Player.physicsBody?.contactTestBitMask = PhysicsCatagory.Enemy
Player.physicsBody?.dynamic = false
var Timer = NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: Selector("SpawnBullets"), userInfo: nil, repeats: true)

let enemy1SpawnDelay = SKAction.waitForDuration(0.5)

SKAction.waitForDuration(0.4)]) , withKey: "SpawnStop")


func didBeginContact(contact: SKPhysicsContact) {

var firstBody : SKPhysicsBody = contact.bodyA
var secondBody : SKPhysicsBody = contact.bodyB
//TJ Section for Coliision with bullets
if (((firstBody.categoryBitMask == && (secondBody.categoryBitMask == PhysicsCatagory.Bullet)) ||
((firstBody.categoryBitMask == PhysicsCatagory.Bullet) && (secondBody.categoryBitMask =={

// CollisionWithTJBoss(firstBody.node as! SKSpriteNode), Bullet: (secondBody.node as! SKSpriteNode)
CollisionWithTJBoss(firstBody.node as! SKSpriteNode, TJ: secondBody.node as! SKSpriteNode)


func CollisionWithBullet(Enemy: SKSpriteNode, Bullet:SKSpriteNode){

ScoreLbl.text = "\(Score)"
func SpawnTJ() {

TJ.physicsBody = SKPhysicsBody(rectangleOfSize: TJ.size)
TJ.physicsBody?.categoryBitMask =
TJ.physicsBody?.contactTestBitMask = PhysicsCatagory.Bullet

TJ.physicsBody?.affectedByGravity = false
TJ.physicsBody?.dynamic = true
TJ.physicsBody?.allowsRotation = false

TJ.position = CGPoint(x: self.size.width / 2, y: self.size.height + 30)
TJ.zPosition = 0.1

//TJ.zPosition = 0
let moveDown = SKAction.moveToY(self.size.height / 2, duration: 5.0)

let moveRight = SKAction.moveByX(200, y: 0, duration: 2.0)
let moveLeft = SKAction.moveByX(-200, y: 0, duration: 3.0)

let reverseMoveRight = moveRight.reversedAction()
let reverseMoveLeft = moveLeft.reversedAction()

let sequence1 = SKAction.sequence([moveRight, reverseMoveRight, moveLeft, reverseMoveLeft])
// let sequence1 = SKAction.sequence([moveLeft, reverseMoveLeft, moveRight, reverseMoveRight])
// let endlessAction = SKAction.repeatActionForever(sequence)

TJ.runAction(SKAction.repeatActionForever(sequence1), withKey: "TJBossEnd")



func CollisionWithTJBoss (Bullet: SKSpriteNode, TJ:SKSpriteNode)


func SpawnBullets(){

let Bullet = SKSpriteNode(imageNamed: "Bullets")
Bullet.zPosition = -2
Bullet.position = CGPointMake(Player.position.x, Player.position.y)

let action = SKAction.moveToY(self.size.height + 15, duration: 0.8)
let actionDone = SKAction.removeFromParent()
Bullet.runAction(SKAction.sequence([action, actionDone]))

Bullet.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet.size)
Bullet.physicsBody?.categoryBitMask = PhysicsCatagory.Bullet
Bullet.physicsBody?.contactTestBitMask = PhysicsCatagory.Enemy
// Bullet.physicsBody?.contactTestBitMask = PhysicsCatagory.Bullet
Bullet.physicsBody?.contactTestBitMask =

Bullet.physicsBody?.affectedByGravity = false
Bullet.physicsBody?.dynamic = false

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

for touch: AnyObject in touches {
let location = touch.locationInNode(self)

Player.position.x = location.x


override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {

for touch: AnyObject in touches {
let location = touch.locationInNode(self)

Player.position.x = location.x



override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */

Answer Source

Your bullets and your enemies have the same categoryBitMAsk value of 1:

static let Enemy : UInt32 = 1 //000000000000000000000000000001
static let Bullet : UInt32 = 0b1 //00000000000000000000000000010

so any tests for Enemy will also match Bullet and vice-versa.

Also, in your didBeginContact, you deal with the collision by calling another function as follows:

CollisionWithTJBoss(firstBody.node as! SKSpriteNode, TJ: secondBody.node as! SKSpriteNode)

and CollisionWithTJBoss is defined as :

func CollisionWithTJBoss (Bullet: SKSpriteNode, TJ:SKSpriteNode)

but you haven't done anything to guarantee the firstBody iOS the bullet and secondBody is TJ (the boss). So when you do Bullet.removeFromParent() in CollisionWithTJBoss i think that the sprite you think is the bullet i.e. firstBody is actually TJ.

For debugging purposes, give your sprites names and in didBeginContact do:

print("First body is ( and second body is (")

To remove a specific sprite, when you don't if it's firstBody or secondBody, use the following:

removeFromParent(contact.bodyA.categoryBitMask == PhysicsCategory.Bullet ? contact.bodyA.node! : contact.bodyB.node!) 

which says if bodyA is the bullet remove it, else remove bodyB

You might find it less confusing to restructure your didBeginContact as follows:

func didBeginContact(contact: SKPhysicsContact) {
    let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

    switch contactMask {
    // TJ and Bullet have contacted
    case | PhysicsCatagory.Bullet :
        let bulletNode = contact.bodyA.categoryBitMask == PhysicsCatagory.Bullet ? contact.bodyA.node! : contact.bodyB.node!
        TJBossHIT -= 1    // ++ is deprecated
        NSLog("Boss and Bullet have Hit")

    default :
        //Some other contact has occurred
        print("Some other contact")

You can just add as many PhysicsCategory.TJ | PhysicsCategory.Bullet cases as you need for all the contacts that you have to take action for in your game. Code each potential contact individually and you won't loose yourself in if...then...else

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download