6

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;
   }
  }
Paul
  • 1,179
  • 3
  • 14
  • 38
  • i can't find let another = whichNode as! GameObjectNode Where does it stay – Cing Nov 12 '15 at 13:07
  • it is in the didBeginContact function on gamescene.swift – Knight0fDragon Nov 12 '15 at 14:56
  • 1
    I do not have my mac with me so I can't test this myself, but does `var player: SKSpriteNode!` initialize player now for us? I do not see anywhere where player is initialized, and I wasnt sure if that did the same thing as `var player = SKSpriteNode()` – Knight0fDragon Nov 12 '15 at 15:01
  • 1
    @Knight0fDragon I'm not sure if `var player: SKSpriteNode!` initializes player for us? I'm not sure either if it does. I'm still pretty new to Swift. I'll play around with it. – Paul Nov 12 '15 at 15:15
  • 1
    Just logged into my mac remotely, I get a Exc_Bad_Instruction because it is not initialized. Do you initialize it somewhere not in the code provided? – Knight0fDragon Nov 12 '15 at 15:23
  • It looks like some code is missing or you messed up the tutorial, why are you making the physicsbody in the did contact? How are you even getting into the didcontact if player hasn't been defined yet, do you have coins overlapping each other or something? – Knight0fDragon Nov 12 '15 at 15:27
  • In the tutorial the property is defined as `var player: SKNode!`. In fact, most properties have the ! after them. I don't really understand why. I'm putting the code in the `didContact` because that is how it is done in the tutorial. – Paul Nov 12 '15 at 16:01
  • Also, I cannot use: ` required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init(size: CGSize) { super.init(size: size) – Paul Nov 12 '15 at 16:01
  • (cont.) because I get an error in the `override func touchesBegan(touches: Set, withEvent event: UIEvent?) {` method. So, I can't follow the tutorial exactly. If you want to view my whole repo, I can send you an invite but I need your email. – Paul Nov 12 '15 at 16:08
  • I tried to use the: `player = createPlayer() foregroundNode.addChild(player)` like in the tutorial, but I get a error as well. – Paul Nov 12 '15 at 16:10
  • @Paul Ok, but creating new player sprite and physics body when collision happens doesn't make any sense. If tutorial really told to put that code there, maybe it's time to find better tutorial. – juniperi Nov 14 '15 at 03:58
  • @juniperi OK, I was wrong. I added another function `createStarAtPosition` and added how the code should be according to the tutorial. I'm still crashing on `let another = whichNode as! GameObjectNode`. I know it's a casting issue, but don't know how to fix it. – Paul Nov 14 '15 at 04:29
  • @Paul Where is the player creation code now? Can't find it anymore. – juniperi Nov 14 '15 at 11:36
  • @juniperi If you want, I can invite you to view the whole repo. I will need your email. – Paul Nov 14 '15 at 19:39
  • @juniperi I took that out because I think it is colliding with other code where the player is created. There is a property `var player: SKSpriteNode!` in GameScene. – Paul Nov 14 '15 at 19:47
  • @Paul what is the error message when your game crashes? And.... so player is created somewhere? Because that one line only creates `SKSpriteNode`, without actual sprite image and physics body. – juniperi Nov 14 '15 at 19:50
  • @juniperi It crashes here `let another = whichNode as! GameObjectNode` in the `didBeginContact`. Something to do with casting I know, but not sure how to fix it. However, I'm trying to implement a coin collection system. That code is from the tutorial I used to try to implement that system in my game. It may not be needed. – Paul Nov 14 '15 at 19:59
  • @Paul Yeah I know that already, but what is the actual error message when it crashes? – juniperi Nov 14 '15 at 20:01
  • @juniperi This is the error in the console `Could not cast value of type 'SKSpriteNode' (0x101334c98) to 'DropCharge.GameObjectNode' (0x1001015d0).` When I add a Swift exception breakpoint. – Paul Nov 14 '15 at 20:05
  • the ! next to a variable means that when the variable is read, it cannot be nil, if it is nil, it will throw an error. You are getting your most recent error because the object you are pulling is of type SKSpriteNode, not of type GameObjectNode, GameObjectNode is like a sibling to SKSpriteNode, the 2 have the same parent, but are not compatible with each other. Now I am sorry, this issue has become a complete mess. I would recommend starting over and trying to understand every line you are trying to write here – Knight0fDragon Nov 16 '15 at 14:47

1 Answers1

2

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


    }
}
RN Vi
  • 68
  • 9