I have a simple breakout game which consists of a node being the ball and some other objects. The problem is when the ball bounces against the wall at a very small angle, it doesn't bounce back, it just slides against the wall indefinitely.
Manually calculating the angle after the contact could solve the problem, though it would be nice to use the physics provided by SpriteKit.
Here is the relevant code:
override func didMove(to view: SKView) {
// screen border setup
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.allowsRotation = true
border.friction = 0
border.restitution = 1
border.linearDamping = 0
border.angularDamping = 0
physicsBody = border
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
physicsWorld.contactDelegate = self
// bottom edge setup
let rect = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: 2)
let bottom = SKNode()
bottom.physicsBody = SKPhysicsBody(edgeLoopFrom: rect)
bottom.physicsBody!.categoryBitMask = BottomCategory
addChild(bottom)
// ball setup
let ball = childNode(withName: "ball") as! SKSpriteNode
ball.physicsBody?.applyImpulse(CGVector(dx: 30, dy: -30))
ball.physicsBody?.allowsRotation = true
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.contactTestBitMask = BlockCategory | BottomCategory
ball.physicsBody?.categoryBitMask = BallCategory
// paddle setup
let paddle = childNode(withName: "paddle") as! SKSpriteNode
paddle.physicsBody!.categoryBitMask = PaddleCategory
}
Related
I'm building a pong/breaker game with a ball and non-static blocks. I'd like the ball to never stop moving, but whenever it hits a block it loses velocity.
I have the restitusion = 1 for all sprites involved, I've tried setting the mass equal to each other and the density and the friction = 0. But, the ball still loses velocity on a bounce.
When the ball hits a block I'm removing it in the didBegin(contact:) function. I've also tried delaying the removal and it didn't help.
I'd like for the ball to have a constant velocity, but still be able to interact with the blocks as later I'd like to add blocks that can be hit without immediately being broken. So, the blocks can't be static but the ball needs to have a constant velocity.
My code for creating the ball node:
func ballNode(_ position: CGPoint?) -> SKSpriteNode {
let node = SKSpriteNode()
node.position = position == nil ? CGPoint(x: size.width/2, y: 100) : position!
node.size = CGSize(width: 17, height: 17)
//background
let background = SKShapeNode(circleOfRadius: 8.5)
background.fillColor = UIColor.white
node.addChild(background)
//physics
node.physicsBody = SKPhysicsBody(circleOfRadius: 8.5)
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.restitution = 1
node.physicsBody?.linearDamping = 0
node.physicsBody?.angularDamping = 0
node.physicsBody?.categoryBitMask = BallCategory
node.physicsBody?.contactTestBitMask = AddBlockBorderCategory | PaddleCategory
node.physicsBody?.collisionBitMask = PaddleCategory | BlockCategory | BorderCategory
return node
}
My code for creating the block node:
func createBlockNode() -> BlockNode {
let width = (size.width-CGFloat(6*layout[0].count))/CGFloat(layout[0].count)
let height = width*0.5
let nodeSize = CGSize(width: width, height: height)
let node = BlockNode(texture: nil, size: nodeSize)
let background = SKShapeNode(rectOf: nodeSize)
background.fillColor = .darkGray
background.strokeColor = .lightGray
//physics
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.restitution = 1
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.categoryBitMask = BlockCategory
node.physicsBody?.contactTestBitMask = BallCategory
node.addChild(background)
return node
}
And a screen recording:
screen recording of the ball losing velocity
I'm starting the ball using this:
ball!.physicsBody?.applyForce(CGVector(dx: 0, dy: 50))
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);
I have a moving platform, but when the node is above the platform it doesnt move with the platform horizontally
In this article, the problem is explained: Moving Platform Hell
http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
And in comment there is solutions for Box2D: Kinematic body
But what about SpriteKit ?
Update
I'm moving the platform using
let moveHPart1 = SKAction.moveByX(origW, y: 0, duration: moveDuration);
let moveHPart2 = SKAction.moveByX(-origW, y: 0, duration: moveDuration);
platform(SKAction.repeatActionForever(SKAction.sequence([moveHPart1, moveHPart2])));
Personally I am against of using physics for moving platforms because moving platform physics body has to be dynamic.
Static platforms
For static platforms setting physics body dynamic property to false is perfect solution. And this is how it is meant to be. Static bodies are not affected by forces but still give you a collision response. So, the problem is solved.
But you can't change position of static physics bodies by using forces. You can do this by using actions or manually setting its position. But, then you are removing a platform out of physics simulation.
In order to do all with physics, you have to keep the platform dynamic. But this can lead in other problems. For example when player lands on platform, he will push the platform down, because player has a mass.
Even if platform has big mass it will go down as time passing. Remember, we cant just update platforms x position manually, because this can make a mess with physics simulation.
"Moving platform hell" as stated in that nice article of LearnCocos2d is probably the best description what can happen when using physics for this task :-)
Moving platform example
To show you some possibilities I made an simple example on how you can move a platform with applying a force to it, and make a character to stay on it.There are few things I've done in order to make this to work:
Changed a mass of platform. This will prevent platform from moving when player bumps in it from below.
Made an edge based physics body to prevent platform falling when player lands on it.
Played with properties like allows rotation and friction to get desired effect.
Here is the code :
import SpriteKit
class GameScene: SKScene,SKPhysicsContactDelegate
{
let BodyCategory : UInt32 = 0x1 << 1
let PlatformCategory : UInt32 = 0x1 << 2
let WallCategory : UInt32 = 0x1 << 3
let EdgeCategory : UInt32 = 0x1 << 4 // This will prevent a platforom from falling down
let PlayerCategory : UInt32 = 0x1 << 5
let platformSpeed: CGFloat = 40.0
let body = SKShapeNode(circleOfRadius: 20.0)
let player = SKShapeNode(circleOfRadius: 20.0)
let platform = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width:100, height:20))
let notDynamicPlatform = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width:100, height:20))
override func didMoveToView(view: SKView)
{
//Setup contact delegate so we can use didBeginContact and didEndContact methods
physicsWorld.contactDelegate = self
//Setup borders
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody?.categoryBitMask = WallCategory
self.physicsBody?.collisionBitMask = BodyCategory | PlayerCategory
//Setup some physics body object
body.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
body.fillColor = SKColor.greenColor()
body.physicsBody = SKPhysicsBody(circleOfRadius: 20)
body.physicsBody?.categoryBitMask = BodyCategory
body.physicsBody?.contactTestBitMask = PlatformCategory
body.physicsBody?.collisionBitMask = PlatformCategory | WallCategory
body.physicsBody?.allowsRotation = false
body.physicsBody?.dynamic = true
self.addChild(body)
//Setup player
player.position = CGPoint(x: CGRectGetMidX(self.frame), y:30)
player.fillColor = SKColor.greenColor()
player.physicsBody = SKPhysicsBody(circleOfRadius: 20)
player.physicsBody?.categoryBitMask = PlayerCategory
player.physicsBody?.contactTestBitMask = PlatformCategory
player.physicsBody?.collisionBitMask = PlatformCategory | WallCategory | BodyCategory
player.physicsBody?.allowsRotation = false
player.physicsBody?.friction = 1
player.physicsBody?.dynamic = true
self.addChild(player)
//Setup platform
platform.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame) - 100)
platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
platform.physicsBody?.categoryBitMask = PlatformCategory
platform.physicsBody?.contactTestBitMask = BodyCategory
platform.physicsBody?.collisionBitMask = BodyCategory | EdgeCategory | PlayerCategory
platform.physicsBody?.allowsRotation = false
platform.physicsBody?.affectedByGravity = false
platform.physicsBody?.dynamic = true
platform.physicsBody?.friction = 1.0
platform.physicsBody?.restitution = 0.0
platform.physicsBody?.mass = 20
//Setup edge
let edge = SKNode()
edge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPoint(x: 0, y:-platform.size.height/2), toPoint: CGPoint(x: self.frame.size.width, y:-platform.size.height/2))
edge.position = CGPoint(x:0, y: CGRectGetMidY(self.frame) - 100)
edge.physicsBody?.categoryBitMask = EdgeCategory
edge.physicsBody?.collisionBitMask = PlatformCategory
self.addChild(edge)
self.addChild(platform)
}
override func update(currentTime: NSTimeInterval) {
if(platform.position.x <= platform.size.width/2.0 + 20.0 && platform.physicsBody?.velocity.dx < 0.0 ){
platform.physicsBody?.velocity = CGVectorMake(platformSpeed, 0.0)
}else if((platform.position.x >= self.frame.size.width - platform.size.width/2.0 - 20.0) && platform.physicsBody?.velocity.dx >= 0.0){
platform.physicsBody?.velocity = CGVectorMake(-platformSpeed, 0.0)
}else if(platform.physicsBody?.velocity.dx > 0.0){
platform.physicsBody?.velocity = CGVectorMake(platformSpeed, 0.0)
}else{
platform.physicsBody?.velocity = CGVectorMake(-platformSpeed, 0.0)
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch: AnyObject? = touches.anyObject()
let location = touch?.locationInNode(self)
if(location?.x > 187.5){
player.physicsBody?.applyImpulse(CGVector(dx: 3, dy: 50))
}else{
player.physicsBody?.applyImpulse(CGVector(dx: -3, dy: 50))
}
}
}
Here is the result :
I can’t find any help or solution for my problem. I have 4 SKSpriteNodes named: bottomGoalGreen, topGoalGreen, bottomGoalBlue, and topGoalBlue. I also have a ball that is a SKSpriteNode named ball. My first question/problem is when I have my ball collide with, for example, topGoalGreen or bottomGoalGreen, I want the topGoalGreen to be removed as well as bottomGoalGreen and then topGoalBlue and bottomGoalBlue to appear and vice versa. My other problem is with my ball and the collision. I have two SKAction.moveToY so the ball can move up and down the screen. I was wondering if the SKActions could be the culprit to why the collision will not happen. I hope I improved my question. If not, I will try again to clarify.
import Foundation
import SpriteKit
import UIKit
struct PhysicsCatagory {
static let bottomGoalGreen : UInt32 = 1
static let topGoalGreen : UInt32 = 2
static let bottomGoalBlue : UInt32 = 4
static let topGoalBlue : UInt32 = 8
static let ball : UInt32 = 16
}
class GamePlayScene: SKScene, SKPhysicsContactDelegate {
var topGoalGreen = SKSpriteNode(imageNamed: "green goal (top).png")
var bottomGoalGreen = SKSpriteNode(imageNamed: "green goal (bottom).png")
var topGoalBlue = SKSpriteNode(imageNamed: "blue goal (top).png")
var bottomGoalBlue = SKSpriteNode(imageNamed: "blue goal (bottom).png")
var ball = SKSpriteNode(imageNamed: "green ball.png")
override func didMoveToView(view: SKView) {
//setup scene
physicsWorld.gravity = CGVector.zeroVector
physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self
self.scene?.backgroundColor = UIColor.blackColor()
self.scene?.size = CGSize(width: 640, height: 1136)
//Top goal green code
topGoalGreen.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 1.52)
topGoalGreen.physicsBody = SKPhysicsBody(rectangleOfSize: topGoalGreen.size)
topGoalGreen.size = CGSize (width: 300, height: 309)
topGoalGreen.physicsBody?.dynamic = false
topGoalGreen.physicsBody?.categoryBitMask = PhysicsCatagory.topGoalGreen
topGoalGreen.physicsBody?.collisionBitMask = 0
topGoalGreen.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(topGoalGreen)
//Bottom goal code
bottomGoalGreen.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 0.252)
bottomGoalGreen.size = CGSize (width: 300, height: 309)
bottomGoalGreen.physicsBody?.dynamic = false
bottomGoalGreen.physicsBody?.categoryBitMask = PhysicsCatagory.bottomGoalGreen
bottomGoalGreen.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(bottomGoalGreen)
//Ball code
ball.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 0.9)
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.width / 2)
ball.size = CGSize (width: 80, height: 82)
ball.physicsBody?.dynamic = true
ball.physicsBody?.categoryBitMask = PhysicsCatagory.ball
ball.physicsBody?.collisionBitMask = 0
ball.physicsBody?.contactTestBitMask = PhysicsCatagory.topGoalGreen
ball.physicsBody?.categoryBitMask = PhysicsCatagory.bottomGoalGreen
ball.physicsBody?.collisionBitMask = PhysicsCatagory.bottomGoalGreen
ball.physicsBody?.contactTestBitMask = PhysicsCatagory.bottomGoalGreen
let moveBallUp = SKAction.moveToY(1040, duration: 2)
let moveBallDown = SKAction.moveToY(90, duration: 2)
let moveUpAndDown = SKAction.sequence([moveBallUp, moveBallDown])
let moveUpAndDownForever = SKAction.repeatActionForever(moveUpAndDown)
ball.runAction(moveUpAndDownForever)
self.addChild(ball)
}
func didBeginContact(contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody = contact.bodyA
var secondBody : SKPhysicsBody = contact.bodyB
if (((firstBody.categoryBitMask == PhysicsCatagory.topGoalGreen) && (secondBody.categoryBitMask == PhysicsCatagory.ball)) ||
((firstBody.categoryBitMask == PhysicsCatagory.ball) && (secondBody.categoryBitMask == PhysicsCatagory.topGoalGreen))){
CollisionWithBall(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)
NSLog("Collision!")
}
}
func CollisionWithBall(topGoalGreen : SKSpriteNode, ball : SKSpriteNode) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
I figured out what I was missing. I was missing a SKPhysicsBody for both goals and ball. Thanks to the other commenters for trying to help me.
//Top goal green code
topGoalGreen.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 1.67)
topGoalGreen.size = CGSize (width: 400, height: 80)
topGoalGreen.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize (width: 10, height: 10))
topGoalGreen.physicsBody?.dynamic = false
topGoalGreen.physicsBody?.categoryBitMask = PhysicsCatagory.topGoalGreen
topGoalGreen.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(topGoalGreen)
In order to register contact you should set contact delegate.
physicsWorld.contactDelegate = self
Otherwise , you can't use methods like didBeginContact or didEndContact. Also you have to set category, contact and collision bitmasks properly in order to make everything to work. So, next thing would be to set category and contact bit masks properly.
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
//Contact will be registered when ball makes a contact with top or bottom goal.
ball.physicsBody?.contactTestBitMask = PhysicsCategory.topGoal | PhysicsCategory.bottomGoal
You have to follow this principle as well for top and bottom goal nodes.
Check out this great answer by rickster in order to understand how things works when using physics engine in SpriteKit.
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.)