SpriteKit won't recognise contact - sprite-kit

bellow is my code which for some reason won't recognise contact between two sprites, in this case, ninja and left or right wall. So my question is what am I missing? I add collision categories and assigned them accordingly
func didBegin(_ contact: SKPhysicsContact) {
let nodeB = contact.bodyB.node!
if nodeB.name == "RIGHT_WALL" {
print("right wall touched")
RightWallTouched = true
self.CurrentScore = self.CurrentScore + 1
}
if nodeB.name == "LEFT_WALL" {
print("left wall touched")
RightWallTouched = false
self.CurrentScore = self.CurrentScore + 1
}
self.CurrentScoreLabel.text = "\(self.CurrentScore)"
if nodeB.name == "SPIKE" {
self.GameStarted = false
if(CurrentScore > HighScore){
defaults.set(CurrentScore, forKey: self.HighScoreStorageKey)
defaults.synchronize()
self.HighScoreLabel.text = "\(self.defaults.integer(forKey: self.HighScoreStorageKey))"
}
endGame()
}
}
func CreateNinja(){
let ninjaTexture = SKTexture(imageNamed: "ninja-right.png")
ninjaTexture.filteringMode = .nearest
self.ninja = SKSpriteNode(texture: ninjaTexture)
self.ninja!.physicsBody = SKPhysicsBody(texture: ninjaTexture, size: ninja!.size)
self.ninja?.name = "NINJA"
self.ninja!.physicsBody?.isDynamic = false
self.ninja!.physicsBody?.allowsRotation = false
self.ninja!.physicsBody?.restitution = 0.6
self.ninja!.physicsBody?.mass = 0.430 // m = 430g
self.ninja!.position = CGPoint(x: self.frame.size.width / 2 , y: self.frame.size.height/5)
self.ninja!.physicsBody?.categoryBitMask = ninjaCollisionCategory
self.ninja!.physicsBody?.contactTestBitMask = 0
self.ninja!.physicsBody?.collisionBitMask = wallCollisionCategory
self.addChild(self.ninja!)
}
func CreateWall(){
let topwall = SKSpriteNode()
topwall.name = "TOP_WALL"
topwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.size.width, height: 5))
topwall.physicsBody?.categoryBitMask = wallCollisionCategory
topwall.physicsBody?.isDynamic = false
topwall.physicsBody?.restitution = 0.6
topwall.position = CGPoint(x: self.frame.size.width / 2.0 , y: self.frame.size.height)
topwall.physicsBody?.friction = 0
topwall.zPosition = 4
self.addChild(topwall)
let leftwall = SKSpriteNode(color: UIColor.black, size: CGSize(width: 20, height: self.frame.size.height))
leftwall.name = "LEFT_WALL"
leftwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 20, height: self.frame.size.height))
leftwall.physicsBody?.categoryBitMask = wallCollisionCategory
leftwall.physicsBody?.contactTestBitMask = 0
leftwall.physicsBody?.isDynamic = false
leftwall.position = CGPoint(x: 0 , y: self.frame.size.height / 2.0)
leftwall.zPosition = 4
leftwall.physicsBody?.restitution = 1.0
leftwall.physicsBody?.friction = 0
self.addChild(leftwall)
let rightwall = SKSpriteNode(color: UIColor.black, size: CGSize(width: 20, height: self.frame.size.height))
rightwall.name = "RIGHT_WALL"
rightwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 20, height: self.frame.size.height))
rightwall.physicsBody?.categoryBitMask = wallCollisionCategory
rightwall.physicsBody?.contactTestBitMask = 0
rightwall.physicsBody?.isDynamic = false
rightwall.physicsBody?.restitution = 1.0
rightwall.physicsBody?.friction = 0
rightwall.position = CGPoint(x: self.frame.size.width , y: self.frame.size.height / 2.0)
rightwall.zPosition = 4
self.addChild(rightwall)
}

A more common way to do the collision function would be something like this
func didBegin(_ contact: SKPhysicsContact) {
let firstBody: SKPhysicsBody
let secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
// Ninja hit wall or wall hit ninja
if (firstBody.categoryBitMask == ninjaCollisionCategory) && (secondBody.categoryBitMask == wallCollisionCategory) {
// Do something for all walls
...
// you can also check the name for some specific wall e.g.
if secondBody.node?.name == "RIGHT_WALL" {
// hit right wall
}
}
}
You also need to set the delegate in didMoveToView
physicsWorld.contactDelegate = self
and should probably set your ninja sprite physics body to dynamic
ninja?.physicsBody?.isDynamic = true // You can also remove this line as its set to true by default
Finally, as good practice, try to follow the Swift guidelines, your functions and properties should start with small letters not capital letters e.g
var currentScore = 0
func createNinja() { ... }
Hope this helps

Related

How do make my player stand on the ground instead of falling through it?

I'm a beginner in game development in Xcode and my player keeps falling through the ground that I added on the GameScene.
In the GameScene I only added the ground as an SKTexture and a player, which is also an SKTexture. There's also a physicsCategories structure.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
struct physicsCategories {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1
static let Ground: UInt32 = 0b100
}
override func didMove(to view: SKView) {
self.backgroundColor = UIColor.blue
self.anchorPoint = CGPoint(x: 0.0, y: 0.0)
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVector(dx: 0, dy: -2)
let player = Player(scene: self)
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody?.categoryBitMask = physicsCategories.Player
player.physicsBody?.contactTestBitMask = physicsCategories.Ground
player.physicsBody?.collisionBitMask = physicsCategories.Ground
player.physicsBody?.affectedByGravity = true
player.physicsBody?.isDynamic = true
addChild(player)
createGrounds()
}
func didBegin(_ contact: SKPhysicsContact) {
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if (body1.categoryBitMask == physicsCategories.Player && body2.categoryBitMask == physicsCategories.Ground) {
body1.node?.position.y = 80
}
}
func createGrounds() {
let backgroundTexture = SKTexture(imageNamed: "ground")
for i in 0 ... 3 {
let background = SKSpriteNode(texture: backgroundTexture)
background.zPosition = 2
background.anchorPoint = CGPoint(x: 0.0, y: 0.0)
background.size = CGSize(width: (self.scene?.size.width)!, height: 250)
background.position = CGPoint(x: (backgroundTexture.size().width * CGFloat(i)), y: -(self.frame.size.height/2))
background.physicsBody = SKPhysicsBody(rectangleOf: background.size)
background.physicsBod
background.physicsBody?.isDynamic = false
background.physicsBody!.affectedByGravity = false
background.physicsBody?.categoryBitMask = physicsCategories.Ground
background.physicsBody?.contactTestBitMask = physicsCategories.Player
background.physicsBody?.collisionBitMask = physicsCategories.Player
background.physicsBody?.applyForce(CGVector(dx: 0, dy: 2))
addChild(background)
let moveLeft = SKAction.moveBy(x: -backgroundTexture.size().width, y: 0, duration: 4)
let moveReset = SKAction.moveBy(x: backgroundTexture.size().width , y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
background.run(moveForever)
}
}
}

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.

Not detecting collision

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).

Multiple physics bodies in SpriteKit

I have a 40x40 rectangle node, and I want to detect when the bottom touches a platform.
I tried this
let feet = SKPhysicsBody(rectangleOfSize: CGSize(width: hero.frame.size.width, height: 1), center: CGPoint(x: 0, y: -(hero.frame.size.height/2 - 0.5)))
then set the categoryBitMask, collisionBitMask, contactTestBitMaskand added it to the hero
hero.physicsBody = SKPhysicsBody(bodies: [feet])
But in didBeginContact the println() doesn't print.
I need a body for the bottom of the rectangle and one for the top, because if hero hits a platform from below the collision should push him down.
Update
Here is how I set the bit masks
let heroFeetCategory: UInt32 = 1 << 0
let edgeCategory: UInt32 = 1 << 1
let groundCategory: UInt32 = 1 << 2
let feet = SKPhysicsBody(rectangleOfSize: CGSize(width: hero.frame.size.width, height: 10), center: CGPoint(x: 0, y: -(hero.frame.size.height/2 - 5)))
feet.collisionBitMask = edgeCategory | groundCategory
feet.contactTestBitMask = groundCategory
feet.categoryBitMask = heroFeetCategory
hero.physicsBody = SKPhysicsBody(bodies: [feet])
hero.physicsBody?.usesPreciseCollisionDetection = true
hero.physicsBody?.velocity = CGVectorMake(0, 0)
hero.physicsBody?.restitution = 0.0
hero.physicsBody?.friction = 0.0
hero.physicsBody?.angularDamping = 0.0
hero.physicsBody?.linearDamping = 1.0
hero.physicsBody?.allowsRotation = false
hero.physicsBody?.mass = 0.0641777738928795
world.addChild(hero)
and for the ground
let ground = SKSpriteNode(color: UIColor.whiteColor(), size: CGSizeMake(38, 38))
ground.name = "groundName"
ground.position = CGPoint(x: 0, y: -(self.frame.size.height/2 - ground.frame.size.height/2))
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size)
ground.physicsBody?.collisionBitMask = edgeCategory | heroFeetCategory
ground.physicsBody?.contactTestBitMask = heroFeetCategory
ground.physicsBody?.categoryBitMask = groundCategory
world.addChild(ground)
And how I detect if they touch
func didBeginContact(contact: SKPhysicsContact) {
var notTheHero: SKPhysicsBody!;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
notTheHero = contact.bodyB;
} else {
notTheHero = contact.bodyA;
}
println(notTheHero.node) // print "heroName"
if (notTheHero.categoryBitMask == groundCategory) {
println("touch began?"); // is never called
}
}
collision is fine, but when you print the node's name through the physics body with notTheHero.node you only access the SKNode, and not the NSString property of its name. Instead, try something like println(notTheHero.node.name);

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.)