Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collect coins and add to score label in Sprite Kit

I'm trying to implement a simple scoring system into my game using this tutorial as a reference:

http://www.raywenderlich.com/87232/make-game-like-mega-jump-sprite-kit-swift-part-2

The problem is, if I try to implement as is, it crashes in the GameScene.swift on this line:

 let another = whichNode as! GameObjectNode

Here are the main parts of the code where the player collects the coins. I can also invite you to my repo if you'd like to take a closer and better look. I know it can be hard from looking at the code I pasted up here.

GameObjectNode.swift:

enum CoinType: Int {
  case Normal = 0
  case Special
}

struct CollisionCategoryBitmask {

  static let Player: UInt32 = 0x00
  static let Coin: UInt32 = 0x01
  static let Platform: UInt32 = 0x02
}

class GameObjectNode: SKNode {

func collisionWithPlayer(player: SKNode) -> Bool {
    return false
}

func checkNodeRemoval(playerY: CGFloat) {
    if playerY > self.position.y + 300.0 {
        self.removeFromParent()
    }
  }
}

class CoinNode: GameObjectNode {

let coinSound = SKAction.playSoundFileNamed("StarPing.wav", waitForCompletion: false)
var coinType: CoinType!

override func collisionWithPlayer(player: SKNode) -> Bool {
    // Boost the player up
    player.physicsBody?.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: 400.0)

    // Play sound
    runAction(coinSound, completion: {
        // Remove this Star
        self.removeFromParent()
    })

    // Award score
    GameState.sharedInstance.score += (coinType == .Normal ? 20 : 100)
    // Award stars
    GameState.sharedInstance.coins += (coinType == .Normal ? 1 : 5)

    // The HUD needs updating to show the new stars and score
    return true
  }
}

GameState.swift

class GameState {
  var score: Int
  var highScore: Int
  var coins: Int

  init() {
    // Init
    score = 0
    highScore = 0
    coins = 0

    // Load game state
    let defaults = NSUserDefaults.standardUserDefaults()

    highScore = defaults.integerForKey("highScore")
    coins = defaults.integerForKey("coins")
  }

  func saveState() {
    // Update highScore if the current score is greater
    highScore = max(score, highScore)

    // Store in user defaults
    let defaults = NSUserDefaults.standardUserDefaults()
    defaults.setInteger(highScore, forKey: "highScore")
    defaults.setInteger(coins, forKey: "coins")
    NSUserDefaults.standardUserDefaults().synchronize()
  }

  class var sharedInstance: GameState {
    struct Singleton {
        static let instance = GameState()
    }

    return Singleton.instance
  }
}

And the GameScene.swift:

import SpriteKit
import CoreMotion
import GameplayKit

struct PhysicsCategory {
 static let None: UInt32              = 0
 static let Player: UInt32            = 0b1      // 1
 static let PlatformNormal: UInt32    = 0b10     // 2
 static let PlatformBreakable: UInt32 = 0b100    // 4
 static let CoinNormal: UInt32        = 0b1000   // 8
 static let CoinSpecial: UInt32       = 0b10000  // 16
 static let Edges: UInt32             = 0b100000 // 32
}

class GameScene: SKScene, SKPhysicsContactDelegate {
 // Other Properties
  ...

 var player: SKSpriteNode!

 // HUD 
 var hudNode: SKNode!
 var lblScore: SKLabelNode!
 var lblCoins: SKLabelNode!

 override func didMoveToView(view: SKView) {
 ....
  // HUD
   hudNode = SKNode()
   hudNode.zPosition = 1000
   cameraNode.addChild(hudNode)

  // Coins
   let coin = SKSpriteNode(imageNamed: "powerup05_1")
   coin.position = convertPoint(CGPoint(x: 300, y: self.size.height-100), toNode: cameraNode)
   coin.zPosition = 1000
   hudNode.addChild(coin)

   lblCoins = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
   lblCoins.fontSize = 70
   lblCoins.fontColor = SKColor.whiteColor()
   lblCoins.position = convertPoint(CGPoint(x: 375, y: self.size.height-100), toNode: cameraNode)
   lblCoins.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
   lblCoins.zPosition = 1000
   lblCoins.text = String(format: "X %d", GameState.sharedInstance.coins)
   hudNode.addChild(lblCoins)

   // Score
   // 4
   lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
   lblScore.fontSize = 70
   lblScore.fontColor = SKColor.whiteColor()
   lblScore.position = convertPoint(CGPoint(x: self.size.width-325, y: self.size.height-100), toNode: cameraNode)
   lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Right
   lblScore.zPosition = 1000
   lblScore.text = "0"
   hudNode.addChild(lblScore)
}

 func setupNodes() {
  ...
    player = fgNode.childNodeWithName("Player") as! SKSpriteNode
 }

   func createStarAtPosition(position: CGPoint, ofType type: CoinType) -> CoinNode {
    // 1
    let node = CoinNode()
    let thePosition = CGPoint(x: position.x * scaleFactor, y: position.y)
    node.position = thePosition
    node.name = "NODE_COIN"

    // 2
    node.coinType = type
    var sprite: SKSpriteNode
    if type == .Special {
        sprite = SKSpriteNode(imageNamed: "CoinSpecial")
    } else {
        sprite = SKSpriteNode(imageNamed: "Coin")
    }
    node.addChild(sprite)

    // 3
    node.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)

    // 4
    node.physicsBody?.dynamic = false
    node.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Coin
    node.physicsBody?.collisionBitMask = 0
    node.physicsBody?.contactTestBitMask = 0

    return node
   }

 func didBeginContact(contact: SKPhysicsContact) {

    let other = contact.bodyA.categoryBitMask == PhysicsCategory.Player ? contact.bodyB : contact.bodyA

    var updateHUD = false

    let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
    // Code crashes here
    let another = whichNode as! GameObjectNode

    updateHUD = another.collisionWithPlayer(player)

    if updateHUD {
        lblCoins.text = String(format: "X %d", GameState.sharedInstance.coins)
        lblScore.text = String(format: "%d", GameState.sharedInstance.score)
    }

   switch other.categoryBitMask {
     case PhysicsCategory.CoinNormal:
       if let coin = other.node as? SKSpriteNode {
         emitParticles("CollectNormal", sprite: coin)
         jumpPlayer()
         runAction(soundCoin)
    }
     case PhysicsCategory.CoinSpecial:
       if let coin = other.node as? SKSpriteNode {
         emitParticles("CollectSpecial", sprite: coin)
         boostPlayer()
         runAction(soundBoost)
     }
    case PhysicsCategory.PlatformNormal:
       if let platform = other.node as? SKSpriteNode {
         if player.physicsBody!.velocity.dy < 0 {
           platformAction(platform, breakable: false)
           jumpPlayer()
           runAction(soundJump)
        }
     }
    case PhysicsCategory.PlatformBreakable:
       if let platform = other.node as? SKSpriteNode {
          if player.physicsBody!.velocity.dy < 0 {
             platformAction(platform, breakable: true)
             jumpPlayer()
             runAction(soundBrick)
      }
    }
       default:
       break;
   }
  }
like image 514
Paul Avatar asked Nov 12 '15 01:11

Paul


1 Answers

I dont understand the code you use in didBeganContact, but you can define the contact bodies in this way:

 enum Ctg:UInt32
{

  case Coin = 1
  case Hero = 2
  case Villain = 4
  case Car = 8

}

var hero = SKSpriteNode()
hero.physicsBody = SKPhysicsBody(rectangleOfSize: hero.size)
hero.physicsBody?.categoryBitMask = Ctg.Hero.rawValue
hero.physicsBody?.contactTestBitMask = Ctg.Coin.rawValue  | Ctg.Villian.rawValue


var coin = SKSpriteNode()
coin.physicsBody = SKPhysicsBody(rectangleOfSize: coin.size)
coin.physicsBody?.categoryBitMask = Ctg.Coin.rawValue
coin.physicsBody?.contactTestBitMask = Ctg.Hero.rawValue 



func didBeginContact(contact: SKPhysicsContact)
{
    var first = SKNode()
    var sec = SKNode()



   // this way you ensure that the first body is the most valuable Ctg (enum)

    if contact.bodyA.node?.physicsBody?.categoryBitMask > contact.bodyB.node?.physicsBody?.categoryBitMask
    {

        first = contact.bodyA.node!
        sec = contact.bodyB.node!

    }
    else
    {

         first = contact.bodyB.node!
          sec = contact.bodyA.node!

    }


    // this part be sure that the category of first it is of most value that sec

    if first.physicsBody!.categoryBitMask == Ctg.Hero.rawValue &&  sec.physicsBody!.categoryBitMask == Ctg.Coin.rawValue
    {
        hero.coins++
        scene.labelCoins.text = String(coins)


    }

    if first.physicsBody!.categoryBitMask == Ctg.Villain.rawValue &&  sec.physicsBody!.categoryBitMask == Ctg.Hero.rawValue
    {
        gameOver = true
        hero.removeFromParent()

        lostGame()


    }
}
like image 84
RN Vi Avatar answered Sep 24 '22 07:09

RN Vi