我正试图在我的游戏中使用本教程作为参考来实现一个简单的评分系统:
http://www.raywenderlich.com/87232/make-game-like-mega-jump-sprite-kit-swift-part-2
问题是,如果我尝试按原样实现,它会在GameScene.swift中崩溃
let another = whichNode as! GameObjectNode
以下是玩家收集硬币的代码的主要部分。如果你想更仔细、更好地看一看,我也可以邀请你参加我的回购。我知道看我贴在这里的代码会很难。
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
}
}
和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;
}
}
我不理解你在didBeganContact中使用的代码,但你可以用这种方式定义联系人主体:
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()
}
}