contact and collision detection in swift - ios

I am working on a game of falling objects and a ball that avoids those objects ,
let ballCatogary:UInt32 = 0x1 << 0
let objectsCatogary:UInt32 = 0x1 << 1
let bottomWallCatogary:UInt32 = 0x1 << 3
1)the ballcatogary is for the ball node that the user controls
2)the objects category is for the right and left walls , and the falling objects that when the user interacts with ,the game stops
3) the bottomcatogary is a sknode that the objects doesn't interact with , but when it passes through it the score value increases
I assigned each node to its categoryBitMask, but now I don't know what to write in the didBeginContact, all i need is help with the did begin collision method, not how to increase the score or end the game, I got that covered.
my code:
//
// PlayScene.swift
// WalkRun
//
// Created by naeim on 7/10/15.
// Copyright (c) 2015 naeim. All rights reserved.
//
import Foundation
import SpriteKit
class PlayScene: SKScene, SKPhysicsContactDelegate{
var ball = SKSpriteNode(imageNamed: "ball")
var wall = SKNode()
var wallRight = SKNode()
var ballSpeed = CGFloat()
let ballCatogary:UInt32 = 0x1 << 0
let objectsCatogary:UInt32 = 0x1 << 1
let bottomWallCatogary:UInt32 = 0x1 << 3
var endOfScreenRight = CGFloat()
var gameOver = 0
var movingObjects = SKNode()
var score = 0
var scoreLabel = SKLabelNode()
var bigWall = SKSpriteNode()
var tallWall = SKSpriteNode()
override func didMoveToView(view: SKView) {
endOfScreenRight = 100
self.physicsWorld.contactDelegate = self
backgroundColor = UIColor(hex: 0x80d9ff)
self.physicsWorld.gravity = CGVectorMake(-9,0)
self.addChild(movingObjects)
//creating the ball
ball.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame) + self.ball.size.height * 2)
ball.physicsBody = SKPhysicsBody(circleOfRadius: self.ball.size.width / 2)
ball.zPosition = 10
ball.physicsBody?.categoryBitMask = ballCatogary
ball.physicsBody?.collisionBitMask = objectsCatogary
ball.physicsBody?.contactTestBitMask = objectsCatogary
self.addChild(ball)
//creating the wall of the left
wall.position = CGPointMake(CGRectGetMinX(self.frame),CGRectGetMinY(self.frame))
wall.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(2, self.frame.size.height * 2.0))
wall.physicsBody?.dynamic = false
wall.physicsBody?.categoryBitMask = objectsCatogary
self.addChild(wall)
//creating the wall of the right
wallRight.position = CGPointMake(CGRectGetMaxX(self.frame), CGRectGetMinY(self.frame))
wallRight.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(2, self.frame.size.height * 2.0))
wallRight.physicsBody?.dynamic = false
wallRight.physicsBody?.categoryBitMask = objectsCatogary
self.addChild(wallRight)
//creating the label
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) )
self.addChild(scoreLabel)
//bottom wall
var bottomWall = SKNode()
bottomWall.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame) + ball.position.y / 1.4)
bottomWall.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width , 1))
bottomWall.physicsBody?.dynamic = false
bottomWall.physicsBody?.affectedByGravity = false
bottomWall.physicsBody?.categoryBitMask = bottomWallCatogary
self.addChild(bottomWall)
var timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("randObject"), userInfo: nil, repeats: true)
}
//function to randomly choose which object
func randObject(){
if gameOver == 0{
var rand = arc4random_uniform(6)+1
switch(rand){
case 1:
leftObject()
case 2:
middleObject()
case 3:
rightObject()
case 4:
LeftAndMiddleObject()
case 5:
rightAndLeftObject()
case 6:
rightAndMiddleObject()
default:
println("error !! non a number other than 0, 1, 2 has been choosen .")
}
}
}
//function that creates a Bigwall
func createBigWall(PositionX:CGFloat , PositionY:CGFloat){
bigWall = SKSpriteNode(imageNamed: "shortwall")
bigWall.position = CGPointMake(PositionX, PositionY)
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: self.bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.affectedByGravity = false
bigWall.physicsBody?.categoryBitMask = objectsCatogary
bigWall.physicsBody?.collisionBitMask = ballCatogary
bigWall.physicsBody?.contactTestBitMask = ballCatogary
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
movingObjects.addChild(bigWall)
}
//function that creates a Tallwall
func createTallWall(PositionX:CGFloat , PositionY:CGFloat ){
tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(PositionX, PositionY)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: self.tallWall.size)
tallWall.physicsBody?.dynamic = false
tallWall.physicsBody?.affectedByGravity = false
tallWall.physicsBody?.categoryBitMask = objectsCatogary
tallWall.physicsBody?.collisionBitMask = ballCatogary
tallWall.physicsBody?.contactTestBitMask = ballCatogary
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
movingObjects.addChild(tallWall)
}
//function to create the left objects
func leftObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMinX(self.frame) + 30, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createTallWall(CGRectGetMinX(self.frame) - 60, PositionY: CGRectGetMaxY(self.frame))
}
}
//function to create the middle objects
func middleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMidX(self.frame) , PositionY: CGRectGetMaxY(self.frame))
}
else
{
createTallWall(CGRectGetMidX(self.frame), PositionY: CGRectGetMaxY(self.frame))
}
}
//function to create the right objects
func rightObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMaxX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createTallWall(CGRectGetMaxX(self.frame) - 60, PositionY: CGRectGetMaxY(self.frame))
}
}
//function to create a right and left object
func rightAndLeftObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMaxX(self.frame) - 30 , PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMinX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createBigWall(CGRectGetMinX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMaxX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
}
func rightAndMiddleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMidX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMaxX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createBigWall(CGRectGetMaxX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMinX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
}
func LeftAndMiddleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMinX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMidX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createBigWall(CGRectGetMidX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMinX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
}
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 == ballCatogary && secondBody.categoryBitMask == objectsCatogary {
gameOver = 1
movingObjects.speed = 0
}
else
{
println("score ++")
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
if gameOver == 0 {
ball.physicsBody?.velocity = CGVectorMake(0, 0)
ball.physicsBody?.applyImpulse(CGVectorMake(70,0))
}
}
override func update(currentTime: NSTimeInterval) {
if gameOver == 0 {
}
}
}

I am not sure what are you asking but check this example code:
//This delegate function will call when two body collide with each other
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 == ballCatogary && secondBody.categoryBitMask == bottomWallCatogary {
//TODO: Replace the log statement with display of Game Over Scene
//add your code here.
}
}
And don't forget to add SKPhysicsContactDelegate at your class declaration and it will look like:
class playScene : SKScene, SKPhysicsContactDelegate {
//your code
}
If you want to detect another collision the you can do it this way:
if firstBody.categoryBitMask == ballCatogary && secondBody.categoryBitMask == objectsCatogary {
gameOver = 1
movingObjects.speed = 0
}
else if firstBody.categoryBitMask == objectsCatogary && secondBody.categoryBitMask == bottomWallCatogary{
score++
println(score)
}
Hope this will help.

Related

Physics of node not working.(Sprite kit)

I am making a spritekit game where you need to move the player back and forth to catch objects falling down the screen. (You gain a point when the player collides with the falling object.)Recently I added restart button function. Before the restart button comes up everything works fine but when I restart the scene and the objects fall to hit the player the player falls off the screen once they make contact. This is odd because I have already set it to make the player (or person) not affected by gravity and not to be dynamic. Again before I press the button it works fine. Why are the characteristics of the person not there once I restart the scene. If you could take a look at my code and tell me why this is happening I would greatly appreciate it.
import SpriteKit
struct physicsCatagory {
static let person : UInt32 = 0x1 << 1
static let Ice : UInt32 = 0x1 << 2
static let IceTwo : UInt32 = 0x1 << 3
static let IceThree : UInt32 = 0x1 << 4
static let Score : UInt32 = 0x1 << 5
}
class GameScene: SKScene, SKPhysicsContactDelegate {
func restartScene(){
self.removeAllChildren()
self.removeAllActions()
scorenumber = 0
lifenumber = 0
createScene()
random()
//spawnThirdIce()
Died = false
}
func createScene(){
physicsWorld.contactDelegate = self
lifenumber = 0
SpeedNumber = 1
BackGround.size = CGSize(width: self.frame.width, height: self.frame.height)
BackGround.position = CGPointMake(self.size.width / 2, self.size.height / 2)
BackGround.zPosition = -5
self.addChild(BackGround)
Score.size = CGSize(width: 2563, height: 1)
Score.position = CGPoint(x: 320, y: -20)
Score.physicsBody = SKPhysicsBody(rectangleOfSize: Score.size)
Score.physicsBody?.affectedByGravity = false
Score.physicsBody?.dynamic = false
Score.physicsBody?.categoryBitMask = physicsCatagory.Score
Score.physicsBody?.collisionBitMask = 0
Score.physicsBody?.contactTestBitMask = physicsCatagory.IceThree
Score.color = SKColor.blueColor()
Score.zPosition = -5
self.addChild(Score)
person.zPosition = 1
person.position = CGPointMake(self.size.width/2, self.size.height/10)
person.setScale(0.6)
person.physicsBody = SKPhysicsBody (rectangleOfSize: CGSize(width: 40, height: 50))
person.physicsBody?.affectedByGravity = false
person.physicsBody?.categoryBitMask = physicsCatagory.person
person.physicsBody?.contactTestBitMask = physicsCatagory.Ice
person.physicsBody?.collisionBitMask = physicsCatagory.Ice
person.physicsBody?.dynamic = false
person.physicsBody?.affectedByGravity = false
ScoreLable = SKLabelNode(fontNamed: "Zapfino")
ScoreLable.position = CGPoint(x: self.frame.width / 2, y: 1700)
ScoreLable.text = "\(scorenumber)"
ScoreLable.fontColor = UIColor.yellowColor()
ScoreLable.fontSize = 150
ScoreLable.fontName = "Zapfino "
self.addChild(ScoreLable)
self.addChild(person)
}
func random() -> CGFloat{
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min min: CGFloat, max: CGFloat) -> CGFloat{
return random() * (max - min) + min
}
var gameArea: CGRect
override init(size: CGSize) {
let maxAspectRatio: CGFloat = 16.0/9.0
let playableWidth = size.height / maxAspectRatio
let margin = (size.width - playableWidth) / 2
gameArea = CGRect(x: margin, y: 0, width: playableWidth, height: size.height)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var timeOfLastSpawn: CFTimeInterval = 0.0
var timePerSpawn: CFTimeInterval = 1.2
var scorenumber = Int()
var lifenumber = Int()
var SpeedNumber : Double = 0.5
var person = SKSpriteNode(imageNamed: "Person1")
let Score = SKSpriteNode()
var ScoreLable = SKLabelNode()
let BackGround = SKSpriteNode (imageNamed: "BackGround")
var restartButton = SKSpriteNode()
var Died = Bool()
override func didMoveToView(view: SKView) {
createScene()
}
func createButton(){
restartButton = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 200, height: 100))
restartButton.position = CGPoint(x: self.gameArea.width/2, y: self.gameArea.height/2)
restartButton.zPosition = 6
self.addChild(restartButton)
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == physicsCatagory.person && secondBody.categoryBitMask == physicsCatagory.IceThree || firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.person{
scorenumber++
if scorenumber == 20 {
timePerSpawn = 1.0
}
if scorenumber == 40{
timePerSpawn = 0.89
}
if scorenumber == 60{
timePerSpawn = 0.6
}
if scorenumber == 80{
timePerSpawn = 0.5
}
if scorenumber == 100{
timePerSpawn = 0.4
}
if scorenumber == 120{
timePerSpawn = 0.3
}
ScoreLable.text = "\(scorenumber)"
CollisionWithPerson(firstBody.node as! SKSpriteNode, Person: secondBody.node as! SKSpriteNode)
}
if firstBody.categoryBitMask == physicsCatagory.Score && secondBody.categoryBitMask == physicsCatagory.IceThree ||
firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.Score{
lifenumber++
if lifenumber == 1{
//person.texture
person.texture = SKTexture (imageNamed: "Flower#2")
}
if lifenumber == 2{
person.texture = SKTexture (imageNamed: "Flower #3")
}
if lifenumber == 3{
createButton()
// self.view?.presentScene(EndScene())
Died = true
}
}
}
func CollisionWithPerson (Ice: SKSpriteNode, Person: SKSpriteNode){
Person.removeFromParent()
}
func spawnThirdIce(){
var Ice = SKSpriteNode(imageNamed: "Ice")
Ice.zPosition = 2
Ice.setScale(1.5)
Ice.physicsBody = SKPhysicsBody(rectangleOfSize: Ice.size)
Ice.physicsBody?.categoryBitMask = physicsCatagory.IceThree
Ice.physicsBody?.contactTestBitMask = physicsCatagory.person | physicsCatagory.Score
Ice.physicsBody?.affectedByGravity = false
Ice.physicsBody?.dynamic = true
let randomXStart = random(min:CGRectGetMinX(gameArea), max: CGRectGetMaxX(gameArea))
let randomXend = random(min:CGRectGetMinX(gameArea),max: CGRectGetMaxX(gameArea))
let startPoint = CGPoint(x: randomXStart, y: self.size.height * 1.2)
let endpoint = CGPoint(x: randomXend, y: -self.size.height * 0.2)
Ice.position = startPoint
let moveEnemy = SKAction.moveTo(endpoint, duration: 2.0)
let deleteEnemy = SKAction.removeFromParent()
let enemySequence = SKAction.sequence([moveEnemy , deleteEnemy])
Ice.runAction(enemySequence)
self.addChild(Ice)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
if Died == true{
if restartButton.containsPoint(location){
restartScene()
}
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if Died == true {
}
else{
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let previousTouch = touch.previousLocationInNode(self)
let ammountDragged = location.x - previousTouch.x
person.position.x += ammountDragged
if person.position.x > CGRectGetMaxX(gameArea) - person.size.width/2{
person.position.x = CGRectGetMaxX(gameArea) - person.size.width/2
}
if person.position.x < CGRectGetMinX(gameArea) + person.size.width/2{
person.position.x = CGRectGetMinX(gameArea) + person.size.width/2
}
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (currentTime - timeOfLastSpawn > timePerSpawn) {
spawnThirdIce()
self.timeOfLastSpawn = currentTime
}
}
}

Keeping an Object from moving beyond a certain point

Im am making a game in sprite kit that requires the user to move the player across the x axis to catch things. I am trying to make it impossible for the player to move off the screen because that looks bad. I have tried to do this with an if statement saying if it moves past a certain point teleport it back to the correct point but am not sure how to do this. That might be the right way to do this but i'm not sure. Please tell / show me what I should do to make this work. I will post the code below.
import SpriteKit
struct physicsCatagory {
static let person : UInt32 = 0x1 << 1
static let Ice : UInt32 = 0x1 << 2
static let IceTwo : UInt32 = 0x1 << 3
static let IceThree : UInt32 = 0x1 << 4
static let Score : UInt32 = 0x1 << 5
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var timeOfLastSpawn: CFTimeInterval = 0.0
var timePerSpawn: CFTimeInterval = 1.2
var scorenumber = Int()
var lifenumber = Int()
var SpeedNumber : Double = 0.5
var person = SKSpriteNode(imageNamed: "Person1")
let Score = SKSpriteNode()
var ScoreLable = SKLabelNode()
let BackGround = SKSpriteNode (imageNamed: "BackGround")
override func didMoveToView(view: SKView) {
self.scene?.backgroundColor = UIColor.blueColor()
physicsWorld.contactDelegate = self
self.scene?.size = CGSize(width: 640, height: 1136)
lifenumber = 0
SpeedNumber = 1
BackGround.size = CGSize(width: self.frame.width, height: self.frame.height)
BackGround.position = CGPointMake(self.size.width / 2, self.size.height / 2)
BackGround.zPosition = -5
self.addChild(BackGround)
Score.size = CGSize(width: 648, height: 1)
Score.position = CGPoint(x: 320, y: -90)
Score.physicsBody = SKPhysicsBody(rectangleOfSize: Score.size)
Score.physicsBody?.affectedByGravity = false
Score.physicsBody?.dynamic = false
Score.physicsBody?.categoryBitMask = physicsCatagory.Score
Score.physicsBody?.collisionBitMask = 0
Score.physicsBody?.contactTestBitMask = physicsCatagory.IceThree
Score.color = SKColor.blueColor()
self.addChild(Score)
person.zPosition = 1
person.position = CGPointMake(self.size.width/2, self.size.height/10)
person.setScale(0.32)
person.physicsBody = SKPhysicsBody (rectangleOfSize: CGSize(width: 40, height: 50))
person.physicsBody?.affectedByGravity = false
person.physicsBody?.categoryBitMask = physicsCatagory.person
person.physicsBody?.contactTestBitMask = physicsCatagory.Ice
person.physicsBody?.collisionBitMask = physicsCatagory.Ice
person.physicsBody?.dynamic = false
ScoreLable = SKLabelNode(fontNamed: "Zapfino")
ScoreLable.position = CGPoint(x: self.frame.width / 2, y: 1000)
ScoreLable.text = "\(scorenumber)"
ScoreLable.fontColor = UIColor.yellowColor()
ScoreLable.fontSize = 100
ScoreLable.fontName = "Zapfino "
self.addChild(ScoreLable)
self.addChild(person)
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == physicsCatagory.person && secondBody.categoryBitMask == physicsCatagory.IceThree || firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.person{
scorenumber++
if scorenumber == 20 {
timePerSpawn = 1.0
}
if scorenumber == 40{
timePerSpawn = 0.89
}
if scorenumber == 60{
timePerSpawn = 0.6
}
if scorenumber == 80{
timePerSpawn = 0.5
}
if scorenumber == 100{
timePerSpawn = 0.4
}
if scorenumber == 120{
timePerSpawn = 0.3
}
ScoreLable.text = "\(scorenumber)"
CollisionWithPerson(firstBody.node as! SKSpriteNode, Person: secondBody.node as! SKSpriteNode)
}
if firstBody.categoryBitMask == physicsCatagory.Score && secondBody.categoryBitMask == physicsCatagory.IceThree ||
firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.Score{
lifenumber++
if lifenumber == 1{
//person.texture
person.texture = SKTexture (imageNamed: "Flower#2")
}
if lifenumber == 2{
person.texture = SKTexture (imageNamed: "Flower #3")
}
if lifenumber == 3{
self.view?.presentScene(EndScene())
}
}
}
func CollisionWithPerson (Ice: SKSpriteNode, Person: SKSpriteNode){
Person.removeFromParent()
}
func spawnThirdIce(){
var Ice = SKSpriteNode(imageNamed: "Ice")
Ice.zPosition = 2
Ice.setScale(0.9)
Ice.physicsBody = SKPhysicsBody(rectangleOfSize: Ice.size)
Ice.physicsBody?.categoryBitMask = physicsCatagory.IceThree
Ice.physicsBody?.contactTestBitMask = physicsCatagory.person | physicsCatagory.Score
Ice.physicsBody?.affectedByGravity = false
Ice.physicsBody?.dynamic = true
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 20
let SpawnPoint = UInt32(MaxValue - MinValue)
Ice.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height)
self.addChild(Ice)
let action = SKAction.moveToY(-85, duration: 2.0)
let actionDone = SKAction.removeFromParent()
Ice.runAction(SKAction.sequence([action,actionDone]))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let previousTouch = touch.previousLocationInNode(self)
let ammountDragged = location.x - previousTouch.x
person.position.x += ammountDragged
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (currentTime - timeOfLastSpawn > timePerSpawn) {
spawnThirdIce()
self.timeOfLastSpawn = currentTime
}
}
}
You might do something like:
if node.position.x > scene.width {
node.position.x = scene.width
}
if node.position.x < 0 {
node.position.x = 0
}

SpriteKit + Swift - Not Firing on contact

So I am playing around with Swift and SpriteKit. I decided to create a simple game where a character will walk to a chest of coins and get all the treasure.
I am trying to detect when the two objects contact eachother, however nothing seems to be happening.
What I did:
1) I set the delegate:
SKPhysicsContactDelegate
2) Created the scene:
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
physicsWorld.contactDelegate = self
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sceneBody.friction = 0
background.zPosition = -20
background.anchorPoint = CGPointZero
background.position = CGPointZero
backgroundRepeated.zPosition = -20
backgroundRepeated.anchorPoint = CGPointZero
backgroundRepeated.position = CGPointMake(background.size.width-1,0)
addChest()
addCharacter()
addChild(background)
addChild(backgroundRepeated)
}
3) Created the items:
func addChest() {
chestOfCoins.anchorPoint = CGPointZero
chestOfCoins.size = CGSize(width: 200,height: 200)
chestOfCoins.position = CGPoint(x: self.frame.width-1, y: 300)
chestOfCoins.zPosition = 20
chestOfCoins.physicsBody = SKPhysicsBody(rectangleOfSize: chestOfCoins.size)
chestOfCoins.physicsBody?.affectedByGravity = false
chestOfCoins.physicsBody?.dynamic = false
chestOfCoins.physicsBody?.categoryBitMask = physicsCategory.chests
chestOfCoins.physicsBody?.collisionBitMask = 0
chestOfCoins.physicsBody?.contactTestBitMask = physicsCategory.character
addChild(chestOfCoins)
}
func addCharacter() {
character.anchorPoint = CGPointZero
character.position = CGPoint(x: 300, y: 300)
character.zPosition = 20
character.size = CGSize(width: 200, height: 200)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character.physicsBody?.affectedByGravity = false
character.physicsBody?.dynamic = false
character.physicsBody?.categoryBitMask = physicsCategory.character
character.physicsBody?.contactTestBitMask = physicsCategory.chests
character.physicsBody?.collisionBitMask = 0
addChild(character)
}
4) created didBeginContact:
func didBeginContact(contact: SKPhysicsContact) {
var contactBody1: SKPhysicsBody
var contactBody2: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
contactBody1 = contact.bodyA
contactBody2 = contact.bodyB
} else {
contactBody1 = contact.bodyB
contactBody2 = contact.bodyA
}
if((contactBody1.categoryBitMask == 1) && (contactBody2.categoryBitMask == 2)) {
print("touched")
} else {
print("not these two")
}
}
FYI, this IF if((contactBody1.categoryBitMask == 1) && (contactBody2.categoryBitMask == 2)) does not output Either the main if block or the else.
5) I created the category physics :
struct physicsCategory {
static let none: UInt32 = 0 //0
static let character: UInt32 = 0b1 //1
static let chests: UInt32 = 0b10 //2
static let snake: UInt32 = 0b100 //4
}
Full GameScene.Swift:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
struct physicsCategory {
static let none: UInt32 = 0 //0
static let character: UInt32 = 0b1 //1
static let chests: UInt32 = 0b10 //2
static let snake: UInt32 = 0b100 //4
static let all: UInt32 = UInt32.max
}
let background = SKSpriteNode(imageNamed: "Backgrounds")
let backgroundRepeated = SKSpriteNode(imageNamed: "Backgrounds")
let chestOfCoins = SKSpriteNode(imageNamed: "chest")
let character = SKSpriteNode(imageNamed: "character")
let gameActions = NSUserDefaults.standardUserDefaults()
func addChest() {
chestOfCoins.anchorPoint = CGPointZero
chestOfCoins.size = CGSize(width: 200,height: 200)
chestOfCoins.position = CGPoint(x: self.frame.width-1, y: 300)
chestOfCoins.zPosition = 20
chestOfCoins.physicsBody = SKPhysicsBody(rectangleOfSize: chestOfCoins.size)
chestOfCoins.physicsBody?.affectedByGravity = false
chestOfCoins.physicsBody?.dynamic = false
chestOfCoins.physicsBody?.categoryBitMask = physicsCategory.chests
chestOfCoins.physicsBody?.collisionBitMask = 0
chestOfCoins.physicsBody?.contactTestBitMask = physicsCategory.character
addChild(chestOfCoins)
}
func addCharacter() {
character.anchorPoint = CGPointZero
character.position = CGPoint(x: 300, y: 300)
character.zPosition = 20
character.size = CGSize(width: 200, height: 200)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character.physicsBody?.affectedByGravity = false
character.physicsBody?.dynamic = false
character.physicsBody?.categoryBitMask = physicsCategory.character
character.physicsBody?.contactTestBitMask = physicsCategory.chests
character.physicsBody?.collisionBitMask = 0
addChild(character)
}
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
physicsWorld.contactDelegate = self
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sceneBody.friction = 0
background.zPosition = -20
background.anchorPoint = CGPointZero
background.position = CGPointZero
backgroundRepeated.zPosition = -20
backgroundRepeated.anchorPoint = CGPointZero
backgroundRepeated.position = CGPointMake(background.size.width-1,0)
addChest()
addCharacter()
addChild(background)
addChild(backgroundRepeated)
}
func didBeginContact(contact: SKPhysicsContact) {
var contactBody1: SKPhysicsBody
var contactBody2: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
contactBody1 = contact.bodyA
contactBody2 = contact.bodyB
} else {
contactBody1 = contact.bodyB
contactBody2 = contact.bodyA
}
if((contactBody1.categoryBitMask == 1) && (contactBody2.categoryBitMask == 2)) {
print("touched")
} else {
print("not these two")
}
}
override func update(currentTime: NSTimeInterval) {
let startWalking = gameActions.boolForKey("Walking")
if (startWalking == true) {
chestOfCoins.position = CGPoint(x: chestOfCoins.position.x - 5, y: chestOfCoins.position.y)
background.position = CGPoint(x: background.position.x - 5, y: background.position.y)
backgroundRepeated.position = CGPoint(x: backgroundRepeated.position.x - 5, y: backgroundRepeated.position.y)
if (background.position.x < -background.size.width) {
background.position = CGPointMake(backgroundRepeated.position.x + backgroundRepeated.size.width, background.position.y)
}
if (backgroundRepeated.position.x < -backgroundRepeated.size.width) {
backgroundRepeated.position = CGPointMake(background.position.x + background.size.width, backgroundRepeated.position.y)
}
}
}
}
Question
Can anyone tell me why, when the screen starts scrolling and those two items come into contact nothing happens and tell me how to fix it?
One also seems to go behind even though I set the zPosition of both to the same
Here is a pic, Don't judge about images :(
set either your chest or your other thingies property to dynamic = true
two non-dynamic physics bodies wont register contacts

save score in game with NSUserDefaults

i am creating a game , but the high score doesn't want to display in the gameScene
and the score is calculated in the playScene , and there in a class called MLPointsLabel to create the label's name and font....etc.
heres my code for the playScene class :
//
// PlayScene.swift
// WalkRun
//
// Created by naeim on 7/10/15.
// Copyright (c) 2015 naeim. All rights reserved.
//
import Foundation
import SpriteKit
class PlayScene: SKScene, SKPhysicsContactDelegate{
var ball = SKSpriteNode(imageNamed: "ball")
var bg = SKSpriteNode(imageNamed: "bg")
var wall = SKNode()
var wallRight = SKNode()
var ballSpeed = CGFloat()
let ballCatogary:UInt32 = 0x1 << 0
let objectsCatogary:UInt32 = 0x1 << 1
let bottomWallCatogary:UInt32 = 0x1 << 3
var endOfScreenRight = CGFloat()
var gameOver = 0
var movingObjects = SKNode()
var score = 0
var scoreLabel = SKLabelNode()
var ballPositionY:CGFloat = 120
var bigWall = SKSpriteNode()
var tallWall = SKSpriteNode()
var levelHardnes:Double = 0.6
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
var num = 0
var highScore = ""
override func didMoveToView(view: SKView) {
//create background
createBackGround()
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVectorMake(-9,0)
self.addChild(movingObjects)
//creating the ball
ball.position = CGPointMake(CGRectGetMidX(self.frame), ballPositionY)
ball.physicsBody = SKPhysicsBody(circleOfRadius: self.ball.size.width / 2)
ball.zPosition = 10
ball.physicsBody?.categoryBitMask = ballCatogary
ball.physicsBody?.collisionBitMask = objectsCatogary
ball.physicsBody?.contactTestBitMask = objectsCatogary
self.addChild(ball)
//creating the wall of the left
wall.position = CGPointMake(CGRectGetMinX(self.frame),CGRectGetMinY(self.frame))
wall.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(2, self.frame.size.height * 2.0))
wall.physicsBody?.dynamic = false
wall.physicsBody?.categoryBitMask = objectsCatogary
self.addChild(wall)
//creating the wall of the right
wallRight.position = CGPointMake(CGRectGetMaxX(self.frame), CGRectGetMinY(self.frame))
wallRight.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(2, self.frame.size.height * 2.0))
wallRight.physicsBody?.dynamic = false
wallRight.physicsBody?.categoryBitMask = objectsCatogary
self.addChild(wallRight)
//creating the label
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.zPosition = 8
scoreLabel.name = "pointsLabel"
scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) )
self.addChild(scoreLabel)
//bottom wall
var bottomWall = SKNode()
bottomWall.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame) + ball.size.width * 1.5 )
bottomWall.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width , 1))
bottomWall.physicsBody?.dynamic = false
bottomWall.physicsBody?.affectedByGravity = false
bottomWall.physicsBody?.categoryBitMask = bottomWallCatogary
bottomWall.physicsBody?.collisionBitMask = objectsCatogary
bottomWall.physicsBody?.contactTestBitMask = objectsCatogary
loadHighScoew()
self.addChild(bottomWall)
var timer = NSTimer.scheduledTimerWithTimeInterval(levelHardnes, target: self, selector: Selector("randObject"), userInfo: nil, repeats: true)
}
func loadHighScoew(){
let defaults = NSUserDefaults.standardUserDefaults()
let highscoreLabel = childNodeWithName("highscoreLabel") as? MLPointLabel
highscoreLabel?.setTo(defaults.integerForKey("highscore"))
}
func createBackGround(){
bg.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
bg.size.width = self.frame.size.width
bg.size.height = self.frame.size.height
self.addChild(bg)
}
func increaseScore(){
score++
println(score)
scoreLabel.text = "\(score)"
}
//function to randomly choose which object
func randObject(){
if gameOver == 0{
var rand = arc4random_uniform(6)+1
switch(rand){
case 1:
leftObject()
case 2:
middleObject()
case 3:
rightObject()
case 4:
LeftAndMiddleObject()
case 5:
rightAndLeftObject()
case 6:
rightAndMiddleObject()
default:
println("error !! non a number other than 0, 1, 2 has been choosen .")
}
}
}
//function that creates a Bigwall
func createBigWall(PositionX:CGFloat , PositionY:CGFloat){
bigWall = SKSpriteNode(imageNamed: "shortwall")
bigWall.position = CGPointMake(PositionX, PositionY)
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: self.bigWall.size)
bigWall.physicsBody?.dynamic = true
bigWall.physicsBody?.affectedByGravity = false
bigWall.physicsBody?.categoryBitMask = objectsCatogary
bigWall.physicsBody?.collisionBitMask = ballCatogary
bigWall.physicsBody?.contactTestBitMask = ballCatogary
bigWall.physicsBody?.collisionBitMask = 0
bigWall.physicsBody?.contactTestBitMask = bottomWallCatogary
bigWall.zPosition = 12
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
movingObjects.addChild(bigWall)
}
//function that creates a Tallwall
func createTallWall(PositionX:CGFloat , PositionY:CGFloat ){
tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(PositionX, PositionY)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: self.tallWall.size)
tallWall.physicsBody?.dynamic = true
tallWall.physicsBody?.affectedByGravity = false
tallWall.physicsBody?.categoryBitMask = objectsCatogary
tallWall.physicsBody?.collisionBitMask = ballCatogary
tallWall.physicsBody?.contactTestBitMask = ballCatogary
tallWall.physicsBody?.collisionBitMask = 0
tallWall.physicsBody?.contactTestBitMask = bottomWallCatogary
tallWall.zPosition = 12
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
movingObjects.addChild(tallWall)
}
//function to create the left objects
func leftObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMinX(self.frame) + 30, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createTallWall(CGRectGetMinX(self.frame) - 60, PositionY: CGRectGetMaxY(self.frame))
}
}
//function to create the middle objects
func middleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMidX(self.frame) , PositionY: CGRectGetMaxY(self.frame))
}
else
{
createTallWall(CGRectGetMidX(self.frame), PositionY: CGRectGetMaxY(self.frame))
}
}
//function to create the right objects
func rightObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMaxX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createTallWall(CGRectGetMaxX(self.frame) - 60, PositionY: CGRectGetMaxY(self.frame))
}
}
//function to create a right and left object
func rightAndLeftObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMaxX(self.frame) - 30 , PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMinX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createBigWall(CGRectGetMinX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMaxX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
}
func rightAndMiddleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMidX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMaxX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createBigWall(CGRectGetMaxX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMinX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
}
func LeftAndMiddleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
createBigWall(CGRectGetMinX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMidX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
else
{
createBigWall(CGRectGetMidX(self.frame) - 30, PositionY: CGRectGetMaxY(self.frame))
createTallWall(CGRectGetMinX(self.frame) + 60, PositionY: CGRectGetMaxY(self.frame))
}
}
func sendHighScore()->String{
return highScore
}
func gameOverDisplay(){
gameOver = 1
movingObjects.speed = 0
if num < score{
num = score
highScore = "\(num)"
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(num, forKey: "highscore")
}
//creating the game Over label
let gameOverScene = GameOverScene(size: self.size, won: false)
self.view?.presentScene(gameOverScene, transition: reveal)
}
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 == ballCatogary && secondBody.categoryBitMask == objectsCatogary {
gameOverDisplay()
}
else if firstBody.categoryBitMask == objectsCatogary || secondBody.categoryBitMask == bottomWallCatogary{
increaseScore()
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
if gameOver == 0 {
ball.physicsBody?.velocity = CGVectorMake(0, 0)
ball.physicsBody?.applyImpulse(CGVectorMake(50,0))
}
}
override func update(currentTime: NSTimeInterval) {
switch(score){
case 25:
levelHardnes = 0.5
ballPositionY += 55
case 50:
levelHardnes = 0.4
ballPositionY += 55
case 100:
levelHardnes = 0.3
ballPositionY += 55
case 250:
levelHardnes = 0.2
ballPositionY += 55
case 300:
ballPositionY += 55
case 400:
ballPositionY += 55
default:
levelHardnes = 0.1
}
}
}
and here is my GameScene code:
//
// GameScene.swift
// WalkRun
//
// Created by naeim on 7/10/15.
// Copyright (c) 2015 naeim. All rights reserved.
//
import SpriteKit
class GameScene: SKScene {
let playButton = SKSpriteNode(imageNamed: "play")
let bg = SKSpriteNode(imageNamed: "bg")
let labelIntro = SKLabelNode()
let playscene = PlayScene()
override func didMoveToView(view: SKView) {
playButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
playButton.zPosition = 3
self.addChild(playButton)
createBackGround()
labelMaker()
playscene.loadHighScoew()
var highScorenum = playscene.sendHighScore()
var highScoreLabel = SKLabelNode()
highScoreLabel.text = "High score : " + highScorenum
highScoreLabel.fontSize = 40
highScoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) - 220)
highScoreLabel.fontName = "Chalkduster"
highScoreLabel.zPosition = 3
self.addChild(highScoreLabel)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches{
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.playButton{
var scene = PlayScene(size: self.size)
let sKview = self.view
sKview?.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
sKview?.presentScene(scene)
}
}
}
func labelMaker(){
labelIntro.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) - 150)
labelIntro.fontName = "Chalkduster"
labelIntro.fontColor = UIColor(hex:000000)
labelIntro.text = "Wall climp"
labelIntro.fontSize = 60
labelIntro.zPosition = 3
self.addChild(labelIntro)
}
func createBackGround(){
bg.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
bg.size.width = self.frame.size.width
bg.size.height = self.frame.size.height
bg.zPosition = 2
self.addChild(bg)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
and here is my MLPointLabel class:
//
// MLPointsLabel.swift
// WalkRun
//
// Created by naeim on 7/16/15.
// Copyright (c) 2015 naeim. All rights reserved.
//
import Foundation
import UIKIt
import SpriteKit
class MLPointLabel:SKLabelNode {
var number = 0
var playScene = PlayScene()
init(num: Int){
super.init()
name = "highscore"
fontColor = UIColor.whiteColor()
fontName = "Chalkduster"
fontSize = 40.0
zPosition = 3
var number = num
text = " High Score :" + "\(num)"
}
func setTo(num:Int){
self.number = num
text = "\(self.number)"
}
func increment(){
number++
text = "\(number)"
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
i really cant quite figure it out why , the game when i run it, doesn't give me any errors , instead it doesn't save the high score
Here I made some changes in your code.
GameScene.swift
//read your score this way
func addHighScoreLbl() {
let highScorenum = NSUserDefaults.standardUserDefaults().integerForKey("highscore")
var highScoreLabel = SKLabelNode()
highScoreLabel.text = "High score : \(highScorenum)"
highScoreLabel.fontSize = 40
highScoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) - 220)
highScoreLabel.fontName = "Chalkduster"
highScoreLabel.zPosition = 3
self.addChild(highScoreLabel)
}
PlayScene.swift
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 == ballCatogary && secondBody.categoryBitMask == objectsCatogary {
let gameOverScene = GameOverScene(size: self.size, won: false)
self.view?.presentScene(gameOverScene, transition: reveal)
}
else if firstBody.categoryBitMask == objectsCatogary || secondBody.categoryBitMask == bottomWallCatogary{
score++
if score > highScore {
//Set your highscore this way.
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "highscore")
}
scoreLabel.text = "\(score)"
}
}
Try
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(num, forKey: "highscore")
and when you are retrieving your highscore in GameScene, it would be better to retrieve it directly from the NSUserDefaults like
let defaults = NSUserDefaults.standardUserDefaults()
var highScorenum = defaults.valueForKey("highscore")!.stringValue
Hope this may help !

Problems with contact/collision in spritekit

I am developing an app where contact plays a big role. I'm my game the "shoe" rest on the ground. I need to be able to know when the shoe is on the ground so it doesn't multi-jump. I also need to know when the shoe hits a hurdle so the game will end. My problem is that it thinks that the ground is a hurdle along with the actual hurdles.
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var shoeGround = SKSpriteNode()
var hurdleTexture = SKTexture()
var hurdlesMoveAndRemove = SKAction()
var hurdlesStopAndRemove = SKAction()
var jump = false
//collision bitmask
let shoeGroundCategory:UInt32 = 0x1 << 0
let hurdleCategory:UInt32 = 0x1 << 28
let groundSensorCategory: UInt32 = 0x1 << 3
override func didMoveToView(view: SKView) {
//Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -8.0)
self.physicsWorld.contactDelegate = self
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ground
var groundTexture = SKTexture(imageNamed:"ground")
var sprite = SKSpriteNode(texture: groundTexture)
//scale it
sprite.setScale(2.1)
//position it
sprite.position = CGPointMake(self.size.width / 2, sprite.size.height / 2)
//add it to the scene
self.addChild(sprite)
//ground variable for the node
var ground = SKSpriteNode()
//set the position of the node
ground.position = CGPointMake(0, groundTexture.size().height)
ground.zPosition = 1000
//set the physics body to equal the size of the image.
ground.physicsBody = SKPhysicsBody(rectangleOfSize:CGSizeMake(self.frame.size.width, groundTexture.size().height * 1.85))
//physics bodies
ground.physicsBody?.dynamic = false
ground.physicsBody?.restitution = CGFloat(0.0)
//add the object to the scene
self.addChild(ground)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ground
var groundSensorTexture = SKTexture(imageNamed:"ground")
var spriteSensor = SKSpriteNode(texture: groundSensorTexture)
//scale it
spriteSensor.setScale(2.1)
//position it
spriteSensor.position = CGPointMake(self.size.width / 2, spriteSensor.size.height / 2)
//add it to the scene
self.addChild(sprite)
//ground variable for the node
var groundSensor = SKSpriteNode()
//set the position of the node
groundSensor.position = CGPointMake(0, groundSensorTexture.size().height)
groundSensor.zPosition = 1000
//set the physics body to equal the size of the image.
groundSensor.physicsBody = SKPhysicsBody(rectangleOfSize:CGSizeMake(self.frame.size.width, groundSensorTexture.size().height * 1.85))
//physics bodies
groundSensor.physicsBody?.dynamic = false
groundSensor.physicsBody?.restitution = CGFloat(0.0)
groundSensor.physicsBody?.categoryBitMask = groundSensorCategory
groundSensor.physicsBody?.contactTestBitMask = shoeGroundCategory | hurdleCategory
//add the object to the scene
self.addChild(ground)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//shoe
var shoeGroundTexture = SKTexture(imageNamed:"default_shoe")
//change texture filtering mode.
shoeGroundTexture.filteringMode = SKTextureFilteringMode.Nearest
//Make the object.
shoeGround = SKSpriteNode(texture: shoeGroundTexture)
//set scale
shoeGround.setScale(0.35)
//position it.
shoeGround.position = CGPointMake(self.frame.size.width * 0.35, ((groundSensorTexture.size().height * 2.0) + (shoeGround.frame.size.height/2)))
shoeGround.zPosition = 100
//give it the collision collider of a circle.
shoeGround.physicsBody = SKPhysicsBody(circleOfRadius: shoeGround.size.height / 2)
shoeGround.physicsBody?.dynamic = true
shoeGround.physicsBody?.allowsRotation = false
shoeGround.physicsBody?.restitution = CGFloat(0.0)
shoeGround.physicsBody?.categoryBitMask = shoeGroundCategory
shoeGround.physicsBody?.contactTestBitMask = groundSensorCategory | hurdleCategory
//add it to the scene
self.addChild(shoeGround)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Hurdles
//Create the Hurdles.
hurdleTexture = SKTexture(imageNamed:"hurdle")
//Spawn the Hurdles.
let spawn = SKAction.runBlock({() in self.spawnHurdles()})
var time = arc4random() % 3
time += 2
let delay = SKAction.waitForDuration(2.0, withRange: 2.0)
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func didBeginContact(contact: SKPhysicsContact) {
var firstBody, secondBody, thirdBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & shoeGroundCategory) != 0 && (secondBody.categoryBitMask & hurdleCategory != 0)) {
//secondBody.node?.removeFromParent()
println("Hurdle")
}
if ((firstBody.categoryBitMask & shoeGroundCategory != 0) && (secondBody.categoryBitMask & groundSensorCategory != 0)) {
//secondBody.node?.removeFromParent()
jump = true
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func didEndContact(contact: SKPhysicsContact) {
var firstBody, 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 & shoeGroundCategory != 0) &&
(secondBody.categoryBitMask & groundSensorCategory != 0)) {
//secondBody.node?.removeFromParent()
jump = false
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
moveHurdles()
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if jump == true {
shoeGround.physicsBody?.velocity = CGVectorMake(0, 0)
shoeGround.physicsBody?.applyImpulse(CGVectorMake(0, 82))
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func spawnHurdles() {
let hurdle = SKSpriteNode(texture: hurdleTexture)
hurdle.setScale(2.0)
hurdle.position = CGPointMake(0, 175)
hurdle.physicsBody = SKPhysicsBody(rectangleOfSize:hurdle.size)
hurdle.physicsBody?.dynamic = false
hurdle.physicsBody?.categoryBitMask = hurdleCategory
hurdle.physicsBody?.contactTestBitMask = shoeGroundCategory | groundSensorCategory
hurdle.runAction(hurdlesMoveAndRemove)
self.addChild(hurdle)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//movement of Hurdles.
func moveHurdles() {
let distanceToMove = CGFloat(self.frame.size.width + 10.0 * hurdleTexture.size().width)
let moveHurdles = SKAction.moveByX(-distanceToMove, y: 0, duration: NSTimeInterval(0.00185 * distanceToMove))
let removeHurdles = SKAction.removeFromParent()
hurdlesMoveAndRemove = SKAction.sequence([moveHurdles, removeHurdles])
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func stopHurdles() {
let distanceToMove = CGFloat(self.frame.size.width + 10.0 * hurdleTexture.size().width)
let stopHurdles = SKAction.moveByX(0, y: 0, duration: NSTimeInterval(0.00185 * distanceToMove))
let removeHurdles = SKAction.removeFromParent()
hurdlesStopAndRemove = SKAction.sequence([stopHurdles, removeHurdles])
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
One way to check if your shoe is on the ground is to implement a condition at the Update function.
The condition would be to check the shoe's y coordinate and if it's below a certain threshold depending on your hurdles and ground.
The second method would be to check the sprite's velocity if it's close to zero.

Resources