Not detecting collision - swift

I'm getting some difficulty detecting collision in my SpriteKit Game. I simply want to detect collision between the missiles and the enemies and boats.
I have the ColliderType:
struct ColliderType {
static let Boat: UInt32 = 1
static let Enemy: UInt32 = 2
static let Wall: UInt32 = 3
static let Bullet: UInt32 = 4
}
class GameplayScene: SKScene, SKPhysicsContactDelegate {
var player = SKSpriteNode()
var enemy = SKSpriteNode()
var boat = SKSpriteNode()
var missile = SKSpriteNode()
The didBegin contact:
func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Missile" {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Missile" && secondBody.node?.name ==
"Enemy" {
incrementScore()
secondBody.node?.removeFromParent()
} else if firstBody.node?.name == "Missile" &&
secondBody.node?.name == "Boat" {
incrementScore()
}
}
I have added the "physicsWorld.contactDelegate = self" in the didMove toview
I have also added physicsBodies to all the relevant spritenodes:
func fireMissile() {
let missile = SKSpriteNode(color: .yellow, size: CGSize(width: 20,
height: 5))
missile.name = "Missile"
missile.position = CGPoint(x: player.position.x + 28, y:
player.position.y + 10)
missile.zPosition = 2
missile.physicsBody = SKPhysicsBody(rectangleOf: missile.size)
missile.physicsBody?.isDynamic = false
missile.physicsBody?.categoryBitMask = ColliderType.Bullet
missile.physicsBody?.collisionBitMask = ColliderType.Enemy |
ColliderType.Boat
missile.physicsBody?.contactTestBitMask = ColliderType.Enemy |
ColliderType.Boat
self.addChild(missile)
}
func createEnemies() {
let enemy = SKSpriteNode(imageNamed: "Enemy1")
enemy.name = "Enemy"
enemy.anchorPoint = CGPoint(x: 0.5, y: 0.5)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.size.height
/ 2)
enemy.physicsBody?.categoryBitMask = ColliderType.Enemy
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.isDynamic = false
enemy.zPosition = 3
enemy.position.y = self.frame.height + 100
enemy.position.x = CGFloat.randomBetweenNumbers(firstNum: -347.5,
secondNum: -85)
self.addChild(enemy)
}
func createBoat() {
let boat = SKSpriteNode(imageNamed: "Boat")
boat.name = "Boat"
boat.anchorPoint = CGPoint(x: 0.5, y: 0.5)
boat.physicsBody = SKPhysicsBody(circleOfRadius: boat.size.height /
2)
boat.physicsBody?.categoryBitMask = ColliderType.Boat
boat.physicsBody?.affectedByGravity = false
boat.physicsBody?.isDynamic = false
boat.zPosition = 3
boat.position.y = self.frame.height + 100
boat.position.x = CGFloat.randomBetweenNumbers(firstNum: 0,
secondNum: 0)
self.addChild(boat)
}

Firstly, some of your categories are wrong:
static let Wall: UInt32 = 3
static let Bullet: UInt32 = 4
This effectively defines Wall as being both a Boat and an Enemy. Change them to:
static let Wall: UInt32 = 4
static let Bullet: UInt32 = 8
(Categories should always be unique powers of 2 - 1, 2, 4, 8, 16 etc).
The rest looks ok, so try that and let us know if it’s working.
Edit:
OK - just noticed that all of your physics bodies have their isDynamic property set to false - this means that, among other things, the body will not trigger contacts. so if you want missile to generate contacts with either enemy or boat, then either missile should be dynamic or both enemy and boat should be dynamic (only 1 of the 2 objects involved in a contact needs to by dynamic).

Related

Swift programming. Possible problem with SKPhysicscontact

I'm working on a project / game and I'm stuck at adding new enemies to the game and to program them. This is how my code looked before I started changing and only had one type of "alien". I want to create two new types, one that you lose 5 points from hitting and one 10. In the code now, there is only one of three and it gives +5 points. How would you guys add the two new? I think I'm overthinking it and that's why I messed up.
import SpriteKit
import GameplayKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate {
var starfield:SKEmitterNode!
var player:SKSpriteNode!
var scoreLabel:SKLabelNode!
var score:Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
var gameTimer:Timer!
var possibleAliens = ["alien"]
//This is the "alien you earn +5 point from shooting. Im trying to get 2 new aliens to the scene, one that you lose -5 points from shooting and one you lose -10 on.
//I have tried to rewrite the codes to the two new, but they still earn +5 points from shooting all 3 types. What would you guys do and how?
let alienCategory:UInt32 = 0x1 << 1
let photonTorpedoCategory:UInt32 = 0x1 << 0
let motionManger = CMMotionManager()
var xAcceleration:CGFloat = 0
override func didMove(to view: SKView) {
starfield = SKEmitterNode(fileNamed: "Starfield")
starfield.position = CGPoint(x: 0, y: 1472)
starfield.advanceSimulationTime(10)
self.addChild(starfield)
starfield.zPosition = -1
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width / 2, y: player.size.height / 2 + 20)
self.addChild(player)
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 100, y: self.frame.size.height - 60)
scoreLabel.fontName = "AmericanTypewriter-Bold"
scoreLabel.fontSize = 36
scoreLabel.fontColor = UIColor.white
score = 0
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
motionManger.accelerometerUpdateInterval = 0.2
motionManger.startAccelerometerUpdates(to: OperationQueue.current!) { (data:CMAccelerometerData?, error:Error?) in
if let accelerometerData = data {
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.25
}
}
}
func addAlien () {
possibleAliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleAliens) as! [String]
let alien = SKSpriteNode(imageNamed: possibleAliens[0])
let randomAlienPosition = GKRandomDistribution(lowestValue: 0, highestValue: 414)
let position = CGFloat(randomAlienPosition.nextInt())
alien.position = CGPoint(x: position, y: self.frame.size.height + alien.size.height)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let animationDuration:TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -alien.size.height), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
alien.run(SKAction.sequence(actionArray))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
fireTorpedo()
}
func fireTorpedo() {
self.run(SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false))
let torpedoNode = SKSpriteNode(imageNamed: "torpedo")
torpedoNode.position = player.position
torpedoNode.position.y += 5
torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
torpedoNode.physicsBody?.isDynamic = true
torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
torpedoNode.physicsBody?.contactTestBitMask = alienCategory
torpedoNode.physicsBody?.collisionBitMask = 0
torpedoNode.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(torpedoNode)
let animationDuration:TimeInterval = 0.3
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
torpedoNode.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & photonTorpedoCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
}
}
func torpedoDidCollideWithAlien (torpedoNode:SKSpriteNode, alienNode:SKSpriteNode) {
let explosion = SKEmitterNode(fileNamed: "Explosion")!
explosion.position = alienNode.position
self.addChild(explosion)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
torpedoNode.removeFromParent()
alienNode.removeFromParent()
self.run(SKAction.wait(forDuration: 2)) {
explosion.removeFromParent()
}
score += 5
}
override func didSimulatePhysics() {
player.position.x += xAcceleration * 50
if player.position.x < -20 {
player.position = CGPoint(x: self.size.width + 20, y: player.position.y)
}else if player.position.x > self.size.width + 20 {
player.position = CGPoint(x: -20, y: player.position.y)
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I wouldn't recommend following this answer if your game becomes more complex. Normally I would say put the aliens in subclasses initialized with their own categorybitmasks and other properties, but to avoid modifying your code too much I just changed the addAlien() method and didBeginContact method. Each alien has its own categoryBitMask and textureName.
let alienCategories:[UInt32] = [(0x1 << 1),(0x1 << 2),(0x1 << 3)]
let alienTextureNames:[String] = ["alien1","alien2","alien3"]
This will make a random Alien using the values in the collections above
func addAlien () {
//Random index between 0 and 2 changing the texture and category bitmask
let index = Int.random(in: 0...2)
let textureName = alienTextureNames[index]
let alien = SKSpriteNode(imageNamed: textureName)
//Use Int.random() if you don't want a CGFLoat
let xPos = CGFloat.random(in: 0...414)
let yPos = frame.size.height + alien.size.height
alien.position = CGPoint(x: xPos, y: yPos)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategories[index]
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let moveAction = SKAction.moveTo(y: -alien.size.height, duration: 6)
alien.run(moveAction, completion: { alien.removeFromParent() })
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[0] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score += 5
}else if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[1] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score -= 5
}else if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[2] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score -= 10
}
}
Change the torpedoNode.physicsBody?.contactTestBitMask = alienCategory line in the fireTorpedo method to alien.physicsBody?.contactTestBitMask = (alienCategories[0] | alienCategories[1] | alienCategories[2])

How to correctly set up collisions in spritekite Swift?

I am creating a game in which balls spawn on top and after they fall down and bounce off the ground(this works),I am working on collisions now, my bullets hit the ball and kind of bounces off. If you need any additional code I will post it. (Edited)
import Foundation
import SpriteKit
Did move to view
struct PhysicsCategory {
static let none: UInt32 = 0b0
static let player: UInt32 = 0b1
static let ball: UInt32 = 0b10
static let bullet: UInt32 = 0b100
static let ground: UInt32 = 0b1000
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
let sceneBody = SKPhysicsBody(edgeLoopFrom: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
player.position = CGPoint(x: self.size.width/2, y: self.size.height / 8)
var timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(spawnBullet), userInfo: nil, repeats: true)
player.size = CGSize(width: 100, height: 100)
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width/2)
player.physicsBody?.affectedByGravity = false
player.physicsBody?.isDynamic = true
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2)
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
bullet.size = CGSize(width: 30, height: 30)
bullet.zPosition = -5
bullet.position = CGPoint(x: player.position.x, y: player.position.y)
let action = SKAction.moveTo(y: self.size.height + 30, duration: 1.0)
bullet.run(SKAction.repeatForever(action))
bullet.physicsBody = SKPhysicsBody(circleOfRadius: bullet.size.width/2)
bullet.name = "bullet"
player.physicsBody?.categoryBitMask = PhysicsCategory.player
player.physicsBody?.collisionBitMask = PhysicsCategory.none
player.physicsBody?.contactTestBitMask = PhysicsCategory.ball
bullet.physicsBody?.categoryBitMask = PhysicsCategory.bullet
bullet.physicsBody?.collisionBitMask = PhysicsCategory.none
bullet.physicsBody?.contactTestBitMask = PhysicsCategory.ball
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bullet
ball.physicsBody?.collisionBitMask = PhysicsCategory.ground
ground.physicsBody?.categoryBitMask = PhysicsCategory.ground
ground.physicsBody?.collisionBitMask = PhysicsCategory.ball
ground.physicsBody?.contactTestBitMask = PhysicsCategory.none
player.name = "player"
addChild(player)
ballSpawner(delay: 2.0)
spawnGround()
}
//didBegin function
func didBegin(_ contact: SKPhysicsContact) {
// print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
I THINK THIS PART IS WRONG
Is my case right? Because when I try to write a bullet and ball it gives me an error - " Binary operator '|' cannot be applied to two 'SKSpriteNode' operands". So only default case is called.
switch contactMask {
//This case never gets called, i can't see it in the console when i run it
case PhysicsCategory.bullet | PhysicsCategory.ball:
let bullet = contact.bodyA.categoryBitMask ==
PhysicsCategory.bullet ? contact.bodyA.node : contact.bodyB.node
let ball = contact.bodyA.categoryBitMask ==
PhysicsCategory.ball ? contact.bodyA.node : contact.bodyB.node
collisionBulletAndBall(ball: (ball)!, bullet: (bullet)!)
print("bullet and ball have contacted.")
case PhysicsCategory.bullet:
print("bullet contact")
case PhysicsCategory.ball & PhysicsCategory.bullet:
print("collision ball with bullet")
default:
print("Some other contact occurred")
}
}
func gameOver() {
print("game over")
}
func collisionBulletAndBall(ball: SKNode, bullet: SKNode) {
ball.removeFromParent()
bullet.removeFromParent()
print("collision")
}
#objc func spawnBullet() {
var bullet = SKSpriteNode(imageNamed: "bullet")
bullet.size = CGSize(width: 30, height: 30)
bullet.zPosition = -5
bullet.position = CGPoint(x: player.position.x, y: player.position.y)
let action = SKAction.moveTo(y: self.size.height + 30, duration: 1.0)
bullet.run(SKAction.repeatForever(action))
bullet.name = "bullet"
self.addChild(bullet)
bullets.append(bullet)
}
func spawnScoreBall() {
var ballSize = CGSize(width: 30, height: 30)
//Random Size
let randomSize = arc4random() % 3
switch randomSize {
case 1:
ballSize.width *= 1.2
ballSize.height *= 1.2
case 2:
ballSize.width *= 1.5
ballSize.height *= 1.5
default:
break
}
var ball = SKSpriteNode(imageNamed: "ball")
ball.size = ballSize
let y = size.height-ballSize.height/2
//Random x
var randomX = CGFloat(arc4random() % UInt32(size.width - ballSize.width))
randomX -= size.width/2-ballSize.width*4
ball.position = CGPoint(x: randomX, y: y)
//Moving logic
// let moveDownAction = SKAction.moveBy(x: 0, y: -size.height + ball.size.height, duration: 2.0)
// let destroyAction = SKAction.removeFromParent()
// let sequenceAction = SKAction.sequence([moveDownAction, destroyAction])
// ball.run(sequenceAction)
//Rotation
var rotateAction = SKAction.rotate(byAngle: 1, duration: 1)
let randomRotation = arc4random() % 2
if randomRotation == 1 {
rotateAction = SKAction.rotate(byAngle: -1, duration: 1)
}
let repeatForeverAction = SKAction.repeatForever(rotateAction)
ball.run(repeatForeverAction)
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2)
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
ball.zPosition = 3
blocks.append(ball)
self.addChild(ball)
}
Spawn ground action
func spawnGround() {
ground = SKSpriteNode(color: UIColor.white, size: CGSize(width: self.frame.size.width, height: 50))
ground.position = CGPoint(x: self.size.width/2, y: 0)
}
player and bullet don't appear to have physics bodies (there is no player.physicsBody = SKPhysicsBody... and bullet.physicsBody = SKPhysicsBody...
ball's physicsbody is assigned way after you assign the physics body's attributes:
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bullet | PhysicsCategory.ground | PhysicsCategory.player
ball.physicsBody?.collisionBitMask = PhysicsCategory.bullet | PhysicsCategory.ground | PhysicsCategory.player
and then later
ball.physicsBody = SKPhysicsBody...
so all those ball.physicsBody assignments did nothing, as physicsBody? was 'nil'
Edit : Your didBegin looks a bit messy. Try this:
func didBegin(_ contact: SKPhysicsContact) {
print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case PhysicsCategory.bullet | PhysicsCategory.ball:
let bullet = contact.bodyA.categoryBitMask == bullet ? contact.bodyA.node : contact.bodyB.node
let ball = contact.bodyA.categoryBitMask == ball ? contact.bodyA.node : contact.bodyB.node
ball.removeFromParent()
bullet.removeFromParent()
print("bullet and ball have contacted.")
default:
print("Some other contact occurred")
}
I removed the setting of the ball's physics body's isDynamic and affectedByGravity properties as you are removing it.

Node not being removed from parent (spritekit)

Create enemy
touchesBegan and didBegin contact function
My enemy node is not being removed from the scene every time my sword node touches it. I'm just wondering if anyone could explain to me what I'm doing wrong?
(UPDATE BELOW)
import SpriteKit
import GameplayKit
import AVFoundation
class LevelTwo: SKScene, SKPhysicsContactDelegate{
var levelBg = SKSpriteNode(imageNamed: "level2")
var hero = SKSpriteNode()
var enemy = SKSpriteNode()
var sword = SKSpriteNode()
var health1 = SKSpriteNode(imageNamed: "playerhplv2")
var health2 = SKSpriteNode(imageNamed: "playerhplv2")
var health3 = SKSpriteNode(imageNamed: "playerhplv2")
var musicPath = URL(fileURLWithPath: Bundle.main.path(forResource: "gameMusic", ofType: "mp3")!)
var musicGamePlayer = AVAudioPlayer()
var runMonster = SKAction()
var waitMonster = SKAction()
var sequenceMonster = SKAction()
var repeatMonster = SKAction()
enum CollisionNum: UInt32{
case swordNum = 1
case enemyNum = 2
case playerNum = 4
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
///music
do{
musicGamePlayer = try AVAudioPlayer(contentsOf: musicPath)
musicGamePlayer.prepareToPlay()
musicGamePlayer.numberOfLoops = -1
musicGamePlayer.play()
}
catch{
print(error)
}
//bg
levelBg.position = CGPoint(x: 0, y: 0)
levelBg.zPosition = 1
levelBg.size = levelBg.texture!.size()
levelBg.setScale(1.25)
self.addChild(levelBg)
//hero
let playerTexture = SKTexture(imageNamed: "main")
hero = SKSpriteNode(texture: playerTexture)
hero.position = CGPoint(x: 0, y: 0)
hero.zPosition = 2
hero.setScale(0.6)
hero.physicsBody = SKPhysicsBody(texture: playerTexture, size: CGSize(width: hero.size.width, height: hero.size.height))
hero.physicsBody!.categoryBitMask = CollisionNum.playerNum.rawValue
hero.physicsBody!.collisionBitMask = CollisionNum.enemyNum.rawValue //player is allowed to bump into rocks and skulls
hero.physicsBody!.contactTestBitMask = CollisionNum.enemyNum.rawValue // same as collisions
hero.physicsBody!.isDynamic = false
self.addChild(hero)
//health1
health1.position = CGPoint(x: 130, y: 150)
health1.zPosition = 3
health1.setScale(0.75)
self.addChild(health1)
//health2
health2.position = CGPoint(x: 230, y: 150)
health2.zPosition = 3
health2.setScale(0.75)
self.addChild(health2)
//health3
health3.position = CGPoint(x: 320, y: 150)
health3.zPosition = 3
health3.setScale(0.75)
self.addChild(health3)
runMonster = SKAction.run(addMonster)
waitMonster = SKAction.wait(forDuration: 0.3)
sequenceMonster = SKAction.sequence([runMonster,waitMonster])
repeatMonster = SKAction.repeatForever(sequenceMonster)
run(repeatMonster)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let locale = touch.location(in: self)
hero.position.x = locale.x
hero.position.y = locale.y
}
}
func addMonster(){
//random position based off the bg size
let monsterHigherX = Int(levelBg.size.width)
let monsterHigherY = Int(levelBg.size.height)
let monsterLowerX = monsterHigherX * -1
let monsterLowerY = monsterHigherY * -1
let randomLocaleX = Int(arc4random_uniform(UInt32(monsterHigherX - monsterLowerX))) + monsterLowerX
let randomLocaleY = Int(arc4random_uniform(UInt32(monsterHigherY - monsterLowerY))) + monsterLowerY
let movementEnemy = SKAction.moveBy(x: -5, y: -5, duration: 0.2)
let movementForever = SKAction.repeatForever(movementEnemy)
let enemyTexture = SKTexture(imageNamed: "boss0")
enemy = SKSpriteNode(texture: enemyTexture)
enemy.zPosition = 2
enemy.setScale(0.5)
enemy.position = CGPoint(x: randomLocaleX, y: randomLocaleY)
enemy.physicsBody = SKPhysicsBody(texture: enemyTexture, size: CGSize(width: enemy.size.width, height: enemy.size.height))
enemy.physicsBody!.isDynamic = true
enemy.physicsBody!.affectedByGravity = false
enemy.physicsBody!.categoryBitMask = CollisionNum.enemyNum.rawValue
enemy.physicsBody!.collisionBitMask = CollisionNum.swordNum.rawValue
enemy.physicsBody!.contactTestBitMask = CollisionNum.swordNum.rawValue
enemy.run(movementForever)
self.addChild(enemy)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let swordTexture = SKTexture(imageNamed: "blade-0")
sword = SKSpriteNode(texture: swordTexture)
sword.setScale(0.50)
sword.zPosition = 2
sword.position = hero.position
sword.physicsBody = SKPhysicsBody(texture: swordTexture, size: CGSize(width: sword.size.width, height: sword.size.height))
sword.physicsBody!.velocity = CGVector(dx: 1200, dy:0)
sword.physicsBody!.isDynamic = true
sword.physicsBody!.affectedByGravity = true
sword.physicsBody!.usesPreciseCollisionDetection = true
sword.physicsBody!.categoryBitMask = CollisionNum.swordNum.rawValue
sword.physicsBody!.collisionBitMask = CollisionNum.enemyNum.rawValue
sword.physicsBody!.contactTestBitMask = CollisionNum.enemyNum.rawValue
self.addChild(sword)
}
func didBegin(_ contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == CollisionNum.swordNum.rawValue | CollisionNum.enemyNum.rawValue {
enemy.removeFromParent()
}
}
override func update(_ currentTime: TimeInterval) {
}
}
(Ive attached my whole level2 class) Thank you so much for the suggestion; however, when I tried implementing this I still run into the same problem (im running this on the iphone simulator) Im wondering whether the error is with my enum or my implementation of my physics with my nodes
You do not want to remove "enemy" because "enemy" is always the last monster you added. You need to check which contactBody is the enemy so you can remove it. You can do that by guaranteeing which node you want to associate as A, and which you want to associate as B by looking at the categoryBitMask value:
func didBegin(_ contact: SKPhysicsContact) {
//This guarantees the lower categoryBitMask (Providing you are only using one) is in A
let bodyA = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
let bodyB = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyB : contact.bodyA
if bodyA.categoryBitMask == CollisionNum.swordNum.rawValue && bodyB.categoryBitMask == CollisionNum.enemyNum.rawValue {
bodyB.node.removeFromParent()
}
}
Of course this will lead to problems with multiple collisions, so instead you may want to do:
var removeNodes = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
//This guarantees the lower categoryBitMask (Providing you are only using one) is in A
let bodyA = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
let bodyB = contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask ? contact.bodyB : contact.bodyA
if bodyA.categoryBitMask == CollisionNum.swordNum.rawValue && bodyB.categoryBitMask == CollisionNum.enemyNum.rawValue {
bodyB.node.moveToParent(removeNodes)
}
}
func didFinishUpdate(){
removeNodes.removeAllChildren()
}
Try this:
func didBegin(_ contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == CollisionNum.swordNum.rawValue | CollisionNum.enemyNum.rawValue {
enemy.removeFromParent()
}
}
You were only testing if bodyA is equal to the enemy, however, bodyA may be equal to the sword instead.

Removing an SKSpriteNode from parent is not working

In SpriteKit in xcode, I'm building a game where a player controls a ball(sprite node) and when the ball touches a "ColorOrbChanger" (sprite node) the player takes on that colour and the orb gets removed, but I'm having trouble figuring out why removeFromParent isn't working for this. thanks in advance :)
class GameScene: SKScene, SKPhysicsContactDelegate {
var RedOrb = SKSpriteNode()
var OrangeOrb = SKSpriteNode()
var YellowOrb = SKSpriteNode()
var GreenOrb = SKSpriteNode()
var BlueOrb = SKSpriteNode()
var PurpleOrb = SKSpriteNode()
func OrbColorPicker() -> SKSpriteNode {
let RedOrb = SKSpriteNode(imageNamed: "ColourNodeRed")
RedOrb.color = SKColor.redColor()
RedOrb.size = CGSize(width: 40, height: 60)
RedOrb.position = CGPoint(x: self.frame.width - 140, y: self.frame.height / 2)
RedOrb.physicsBody = SKPhysicsBody(rectangleOfSize: RedOrb.size)
RedOrb.physicsBody?.categoryBitMask = PhysicsCatagory.RedOrb
RedOrb.physicsBody?.contactTestBitMask = PhysicsCatagory.Player
RedOrb.physicsBody?.affectedByGravity = false
RedOrb.physicsBody?.dynamic = false
RedOrb.runAction(moveAndRemove)
RedOrb.zPosition = 2
/* ^ plus all the other colours Orange, Yellow, Green, Blue, Purple, then i added all those into an array that spat out a random colour orb) */
var ColorOrbArray = [RedOrb, OrangeOrb, YellowOrb, GreenOrb, BlueOrb, PurpleOrb]
let countArray = UInt32(ColorOrbArray.count)
let pickOneNode = arc4random_uniform(countArray)
let randomElement = Int(pickOneNode)
if randomElement == 0{
self.addChild(RedOrb)
}
if randomElement == 1{
self.addChild(OrangeOrb)
}
if randomElement == 2{
self.addChild(YellowOrb)
}
if randomElement == 3{
self.addChild(GreenOrb)
}
if randomElement == 4{
self.addChild(BlueOrb)
}
if randomElement == 5{
self.addChild(PurpleOrb)
}
return ColorOrbArray[randomElement]
func didBeginContact(contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == PhysicsCatagory.Player) && (contact.bodyB.categoryBitMask == PhysicsCatagory.RedOrb) || (contact.bodyA.categoryBitMask == PhysicsCatagory.RedOrb) && (contact.bodyB.categoryBitMask == PhysicsCatagory.Player){
let changeColorActionRed = SKAction.colorizeWithColor(SKColor.redColor(), colorBlendFactor: 1.0, duration: 0)
Player.runAction(changeColorActionRed)
RedOrb.removeFromParent()
}
}

Collision not working in swift + sprite kit

import SpriteKit
// fix spawning so close to the middle
// get ball col working
// set width apart/ height they must be apart (if statement)
let BallCategoryName = "ball"
let BarCategoryName = "block"
let BarNodeCategoryName = "blockNode"
let GreenBallCategory : UInt32 = 0x1 << 0
let RedBallCategory: UInt32 = 0x1 << 1
let GreenBarCategory : UInt32 = 0x1 << 2
let RedBarCategory : UInt32 = 0x1 << 3
class GameScene: SKScene, SKPhysicsContactDelegate {
//VARIABLES IMPORTANTE//
var greenBall = SKSpriteNode(imageNamed: "greenball")
var redBall = SKSpriteNode(imageNamed: "redball")
var bar0 = SKSpriteNode(imageNamed: "bar0")
var bar1 = SKSpriteNode(imageNamed: "bar1")
var bar2 = SKSpriteNode(imageNamed: "bar2")
var bar3 = SKSpriteNode(imageNamed: "bar3")
var bar4 = SKSpriteNode(imageNamed: "bar4")
var bar5 = SKSpriteNode(imageNamed: "bar5")
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
// BALL CHARACTERISTICS//
//Green//
greenBall.position = CGPoint(x: frame.size.width*0.25, y: (frame.size.height * 0.95)/2)
greenBall.anchorPoint = CGPoint(x: 0.5, y: 0.5)
greenBall.physicsBody = SKPhysicsBody(circleOfRadius: greenBall.size.width/2)
greenBall.physicsBody?.friction = 0
greenBall.physicsBody?.restitution = 1
greenBall.physicsBody?.linearDamping = 0
greenBall.physicsBody?.angularDamping = 0
greenBall.physicsBody?.affectedByGravity = false
greenBall.name = BallCategoryName
addChild(greenBall)
//Red//
redBall.anchorPoint = CGPoint(x: 0.5 , y: 0.5)
redBall.position = CGPoint(x: frame.size.width*0.75 , y: (frame.size.height * 0.95)/2)
redBall.physicsBody = SKPhysicsBody(circleOfRadius: greenBall.size.width/2)
redBall.physicsBody?.friction = 0
redBall.physicsBody?.restitution = 1
redBall.physicsBody?.linearDamping = 0
redBall.physicsBody?.angularDamping = 0
redBall.physicsBody?.affectedByGravity = false
redBall.physicsBody?.dynamic = false
addChild(redBall)
//////////////////////////////////////////////////////
// SETTING UP SCENE//
GameScene(size: self.view!.frame.size) //WHy not work
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
// BACKGROUND COLOR
// PHYSICS //
physicsWorld.gravity = CGVectorMake(0, 0)
//////////////////////////////////////////////////////
// EXTRA BARS
var midBar = SKShapeNode(rectOfSize: CGSize(width : frame.size.width, height: 3))
var menuBar = SKShapeNode(rectOfSize: CGSize(width : frame.size.width, height: 3))
var invisMenuBar = SKShapeNode(rectOfSize: CGSize(width: frame.size.width, height: 0.00001))
var botBar = SKShapeNode(rectOfSize: CGSize(width : frame.size.width, height: 3))
midBar.fillColor = SKColor.whiteColor()
menuBar.fillColor = SKColor.whiteColor()
invisMenuBar.fillColor = SKColor.purpleColor()
botBar.fillColor = SKColor.whiteColor()
////MID BAR////
var minus = frame.size.height * 0.049
midBar.position = CGPoint(x: frame.size.width/2, y: (frame.size.height * 0.95)/2)
addChild(midBar)
////BOT BAR////
botBar.position = CGPoint(x: frame.size.width/2, y: 0 + (botBar.frame.size.height/2))
addChild(botBar)
////MENU BAR////
menuBar.position = CGPoint(x: frame.size.width/2, y: frame.size.height - (frame.size.height * 0.05))
addChild(menuBar)
//// INVIS MENU BAR ////
invisMenuBar.position = CGPoint(x: frame.size.width/2, y: frame.size.height - (frame.size.height * 0.047))
invisMenuBar.physicsBody = SKPhysicsBody(rectangleOfSize: invisMenuBar.frame.size)
invisMenuBar.physicsBody?.friction = 0
invisMenuBar.physicsBody?.restitution = 1
invisMenuBar.physicsBody?.linearDamping = 0
invisMenuBar.physicsBody?.angularDamping = 0
invisMenuBar.physicsBody?.affectedByGravity = false
invisMenuBar.physicsBody?.dynamic = false
invisMenuBar.zPosition = 100
menuBar.zPosition = 5
addChild(invisMenuBar)
// TEST STUFF WITH BARS //
let chosenOne = SKSpriteNode(imageNamed: "bar4")
chosenOne.position = CGPoint(x: frame.midX - chosenOne.frame.width/2 - 50 , y: self.frame.height * 0.75)
addChild(chosenOne)
//////////////////////////////////////////////////////
//// SETTING UP MY CONTACT ///
greenBall.physicsBody?.categoryBitMask = GreenBallCategory
chosenOne.physicsBody?.categoryBitMask = RedBarCategory
greenBall.physicsBody?.contactTestBitMask = RedBarCategory
chosenOne.physicsBody?.contactTestBitMask = GreenBallCategory
chosenOne.physicsBody = SKPhysicsBody(rectangleOfSize: chosenOne.frame.size)
func didBeginContact(contact: SKPhysicsContact) {
// 1. Create local variables for two physics bodies
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
// 2. Assign the two physics bodies so that the one with the lower category is always stored in firstBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
// 3. react to the contact between ball and bottom
if firstBody.categoryBitMask == GreenBallCategory && secondBody.categoryBitMask == RedBarCategory {
//TODO: Replace the log statement with display of Game Over Scene
println("Hit bottom. First contact has been made.")
}
}
I'm following http://www.raywenderlich.com/84341/create-breakout-game-sprite-kit-swift to the letter. I've got the self contact delegate all set up, the two do hit each other but it doesn't print to the logs to prove the collision stuff is actually working.
Any ideas?
Your didBeginContact method is inside the didMoveToView method. Since both methods in SKPhysicsContactDelegate are optional:
protocol SKPhysicsContactDelegate : NSObjectProtocol {
optional func didBeginContact(contact: SKPhysicsContact)
optional func didEndContact(contact: SKPhysicsContact)
}
you're not being warned that GameScene doesn't implement the methods.
Move didBeginContact out of didMoveToView and you should be fine.
(On a side note: I would advise that you split up didMoveToView into several, smaller methods. It'll make it easier to understand what's going on because, at the moment, it looks pretty bloated.)