这篇文章的游戏使用SpriteKit和Swift语言来完成。
SpriteKit是苹果自己的游戏引擎,更能贴合iOS系统底层的API,不过架构和实现上都是模仿了Cocos2D。所以使用上其实差别不大,不过SpriteKit更轻量级一些。
override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() var skView : SKView = self.view as SKView if !skView.scene { //DEBUG skView.showsFPS = true skView.showsNodeCount = true var scene : SKScene = GameScene.sceneWithSize(skView.bounds.size) scene.scaleMode = .AspectFill skView.presentScene(scene) } }
func setupMedia() { var error : NSError? let backgroundMusicURL : NSURL = NSBundle.mainBundle().URLForResource(BG_MUSIC_NAME, withExtension: "caf") backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: backgroundMusicURL , error: &error) if error { println("load background music error : \(error)") } else { backgroundMusicPlayer!.numberOfLoops = -1 backgroundMusicPlayer!.prepareToPlay() backgroundMusicPlayer!.play() } }
override func viewDidLoad() { super.viewDidLoad() setupMedia() }
class GameOverScene : SKScene { convenience init(size: CGSize, won: Bool) { self.init(size: size) self.backgroundColor = SKColor(red:1.0, green:1.0, blue:1.0, alpha:1.0) self.setupMsgLabel(isWon :won) self.directorAction() } func setupMsgLabel(isWon won: Bool) { var msg: String = won ? "Yow Won!" : "You Lose :[" var msgLabel = SKLabelNode(fontNamed: "Chalkduster") msgLabel.text = msg msgLabel.fontSize = 40 msgLabel.fontColor = SKColor.blackColor() msgLabel.position = CGPointMake(self.size.width/2, self.size.height/2) self.addChild(msgLabel) } func directorAction() { var actions: AnyObject[] = [ SKAction.waitForDuration(3.0), SKAction.runBlock({ var reveal = SKTransition.flipHorizontalWithDuration(0.5) var gameScene = GameScene(size: self.size) self.view.presentScene(gameScene, transition: reveal) }) ] var sequence = SKAction.sequence(actions) self.runAction(sequence) } }
var player: SKSpriteNode! //英雄精灵 var lastSpawnTimeInterval: NSTimeInterval! //记录上次时间和更新时间 var lastUpdateTimeInterval: NSTimeInterval! var monstersDestroyed: Int! //记录被消灭的怪兽数量
init(size: CGSize) { super.init(size: size) self.backgroundColor = SKColor(red: 1.0, green:1.0, blue:1.0, alpha:1.0) player = SKSpriteNode(imageNamed: "player") player.position = CGPointMake(self.player.size.width/2, self.frame.size.height/2) self.addChild(player) monstersDestroyed = 0 lastSpawnTimeInterval = 0 lastUpdateTimeInterval = 0 gameLevel.nextLevel() //physics self.physicsWorld.gravity = CGVectorMake(0, 0) self.physicsWorld.contactDelegate = self }
func addMonster() { var monster = SKSpriteNode(imageNamed: "monster") //location var minY = monster.size.height/2 var maxY = self.frame.size.height - monster.size.height/2 var rangeY = maxY - minY var actualY = arc4random() % rangeY + minY monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualY) self.addChild(monster) //physics monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.size) monster.physicsBody.dynamic = true monster.physicsBody.categoryBitMask = monsterCategory monster.physicsBody.contactTestBitMask = projectileCategory monster.physicsBody.collisionBitMask = 0 //speed var minDuration = 2.0 var maxDuration = 4.0 var rangeDuration = maxDuration - minDuration var actualDuration = arc4random() % rangeDuration + minDuration var actionMove = SKAction.moveTo(CGPointMake(-monster.size.width/2, actualY), duration: actualDuration) var actionMoveDone = SKAction.removeFromParent() var loseAction = SKAction.runBlock({ var reveal = SKTransition.flipHorizontalWithDuration(0.5) var gameOverScene = GameOverScene(size: self.size, won: false) self.view.presentScene(gameOverScene, transition: reveal) }) monster.runAction(SKAction.sequence([actionMove, loseAction, actionMoveDone])) }
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) { // get touch var touch = touches.anyObject() as UITouch var location = touch.locationInNode(self) //bullet action self.addProjectile(location: location) }
func addProjectile(#location: CGPoint) { var projectile = SKSpriteNode(imageNamed:"projectile") projectile.position = player.position //physics projectile.physicsBody = SKPhysicsBody(circleOfRadius: projectile.size.width/2) projectile.physicsBody.dynamic = true projectile.physicsBody.categoryBitMask = projectileCategory projectile.physicsBody.contactTestBitMask = monsterCategory projectile.physicsBody.collisionBitMask = 0 projectile.physicsBody.usesPreciseCollisionDetection = true var offset = niSub(location, projectile.position) if offset.x < 0 {return} self.addChild(projectile) // direct unit vector var direction = niNormalize(offset) //to screen's edge var shootAmount = niMult(direction, 1000) //now loc var realDest = niAdd(shootAmount, projectile.position) //action var velocity = 480.0/1.0 var realMoveDuration = Double(self.size.width) / velocity var actionMove = SKAction.moveTo(realDest, duration: realMoveDuration) var actionMoveDone = SKAction.removeFromParent() var sequence = SKAction.sequence([actionMove, actionMoveDone]) projectile.runAction(sequence) self.runAction(SKAction.playSoundFileNamed("pew-pew-lei.caf", waitForCompletion: false)) }
// overload @infix func %(lhs: UInt32, rhs: Float) -> Float { return Float(lhs) % Float(rhs) } @infix func %(lhs: UInt32, rhs: Double) -> Double { return Double(lhs) % Double(rhs) } let niAdd = {(a: CGPoint, b: CGPoint) -> CGPoint in CGPointMake(a.x + b.x, a.y + b.y)} let niSub = {(a: CGPoint, b: CGPoint) -> CGPoint in CGPointMake(a.x - b.x, a.y - b.y)} let niMult = {(a: CGPoint, b: Float) -> CGPoint in CGPointMake(a.x * b, a.y * b)} let niLength = {(a: CGPoint) -> CGFloat in CGFloat(sqrt(Double(a.x * a.x + a.y * a.y)))}
// unit vector let niNormalize = {(a : CGPoint) -> CGPoint in var length = niLength(a) return CGPointMake(a.x / length, a.y / length) }
override func update(currentTime: NSTimeInterval) { var timeSinceLast: CFTimeInterval = currentTime - lastSpawnTimeInterval lastUpdateTimeInterval = currentTime if timeSinceLast > 1 { timeSinceLast = Double(gameLevel.toRaw()) / 60.0 lastUpdateTimeInterval = currentTime } self.updateWithTimeSinceLastUpdate(timeSinceLast: timeSinceLast) }
func updateWithTimeSinceLastUpdate(#timeSinceLast: CFTimeInterval) { lastSpawnTimeInterval = lastSpawnTimeInterval + timeSinceLast if lastSpawnTimeInterval > 1 { lastSpawnTimeInterval = 0 self.addMonster() } }
func didBeginContact(contact: SKPhysicsContact) { var firstBody: SKPhysicsBody! var 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 & projectileCategory) != 0 && (secondBody.categoryBitMask & monsterCategory) != 0 { self.didCollide(projectile: firstBody.node as SKSpriteNode, monster: secondBody.node as SKSpriteNode) } }
func didCollide(#projectile: SKSpriteNode, monster: SKSpriteNode) { projectile.removeFromParent() monster.removeFromParent() monstersDestroyed = monstersDestroyed + 1 if monstersDestroyed > 30 { var reveal = SKTransition.flipHorizontalWithDuration(0.5) var gameOverScene = GameOverScene(size: self.size, won: true) self.view.presentScene(gameOverScene, transition: reveal) } }