概述

使用SpriteKit实现一个简单的游戏, 通过一个游戏来进行SpriteKit的入门, 熟练2D游戏的API, 也可以更好的结合在iOS应用中.

详细

代码下载:http://www.demodashi.com/demo/10667.html

今天我们进入一个全新的系列,先熟悉SpriteKit,然后再看实战的游戏案例。

一、了解SpriteKit

本期的内容就是使用SpriteKit实现一个简单的游戏, 通过一个游戏来进行SpriteKit的入门, 熟练2D游戏的API, 也可以更好的结合在iOS应用中, SpriteKit和Cocos2d 一样都是一个模式, 就是场景, 精灵之类的, 也都是操作Node节点, 我们先来看下基本的API, 以便更好的理解.

SKNode

open class SKNode : UIResponder, NSCopying, NSCoding, UIFocusItemopen var name: String?open var position: CGPointopen var zPosition: CGFloatopen var zRotation: CGFloatopen func addChild(_ node: SKNode)open func removeFromParent()open func enumerateChildNodes(withName name: String, using block: @escaping (SKNode, UnsafeMutablePointer<ObjCBool>) -> Swift.Void)open func run(_ action: SKAction, completion block: @escaping () -> Swift.Void)open func run(_ action: SKAction, withKey key: String)open func hasActions() -> Boolopen func removeAllActions()open func intersects(_ node: SKNode) -> Bool

SpriteKit中所有节点的父类, 不直接操作, 类似于CAAnimation

  • name: 节点的名字, 对节点进行标识

  • position: 节点的位置, SpriteKit坐标系与UIKit不同

  • zPosition: Z轴上的位置, 用于显示节点的前后顺序

  • zRotation: 旋转的弧度

  • addChild: 添加子节点, 类似于addSubView

  • removeFromParent: 删除子节点, 类似于removeFromSuperView

  • enumerateChildNodes: 遍历子节点

  • run: 执行行动

  • hasActions: 是否有行动

  • removeAllActions: 删除所有行动

  • intersects: 节点之间是否有交集

SKSpriteNode

open class SKSpriteNode : SKNode, SKWarpablepublic convenience init(imageNamed name: String)

Sprite和UIkit中的ImageView非常相似, 游戏中的每一个显示出来的图像都可以使用精灵

SKTexture

open class SKTexture : NSObject, NSCopying, NSCoding

纹理, 目前可知可以实现类似imageView的动画效果, 待深究

SKShapeNode

open class SKShapeNode : SKNodeopen var path: CGPath?open var strokeColor: UIColoropen var fillColor: UIColoropen var lineWidth: CGFloat

与CAShapeLayer及Quartz2D的部分API极度相似, 和H5中的Canvas也很类似

SKLabelNode

open class SKLabelNode : SKNode public convenience init(text: String?)public init(fontNamed fontName: String?)open var verticalAlignmentMode: SKLabelVerticalAlignmentModeopen var horizontalAlignmentMode: SKLabelHorizontalAlignmentModeopen var fontSize: CGFloatopen var fontColor: UIColor?

类似于UILabel, 与字体颜色和对齐属性等字段, 垂直对齐包括上, 中, 基线, 下, 水平对齐包括,左, 中, 右

SKCameraNode

open class SKCameraNode : SKNode

摄像机节点, 摄像机的position永远在屏幕中

SKAction

open class SKAction : NSObject, NSCopying, NSCodingopen class func moveBy(x deltaX: CGFloat, y deltaY: CGFloat, duration sec: TimeInterval) -> SKActionopen class func move(to location: CGPoint, duration sec: TimeInterval) -> SKActionopen class func rotate(byAngle radians: CGFloat, duration sec: TimeInterval) -> SKActionopen class func rotate(toAngle radians: CGFloat, duration sec: TimeInterval) -> SKActionopen class func scale(by scale: CGFloat, duration sec: TimeInterval) -> SKActionopen class func scale(to scale: CGFloat, duration sec: TimeInterval) -> SKActionopen class func sequence(_ actions: [SKAction]) -> SKActionopen class func group(_ actions: [SKAction]) -> SKActionopen class func `repeat`(_ action: SKAction, count: Int) -> SKActionopen class func repeatForever(_ action: SKAction) -> SKActionopen class func wait(forDuration sec: TimeInterval) -> SKActionopen class func removeFromParent() -> SKActionopen class func run(_ block: @escaping () -> Swift.Void) -> SKAction

Action和Core Animataion非常相似, 包括了移动, 旋转, 缩放的动画

  • sequence 行动序列, 逐个执行行动, 串行

  • group 组, 类似于CAAnimationGroup 将行动包装在组中同时执行, 并行

  • repeat(:count:) 重复次数

  • repeatForever: 一直重复执行

  • wait: 等待执行

  • removeFromParent: 移除行动

  • run: 类似于基本动画的animate闭包, 执行闭包中的动作

SKSence

open class SKScene : SKEffectNodepublic init(size: CGSize)open var scaleMode: SKSceneScaleMode
@available(iOS 9.0, *)weak open var camera: SKCameraNode?open var anchorPoint: CGPointopen func update(_ currentTime: TimeInterval)open func didEvaluateActions()open func didMove(to view: SKView)

Sence类似于ViewController, 可以进行场景的切换

  • size: 场景的尺寸

  • scaleMode: 缩放模式

  • camera: 摄像头

  • anchorPoint: 锚点, 和Layer层相同, 但坐标系不同

  • update: 刷帧, 每一帧都会调用的方法用于渲染

  • didEvaluateActions: 生命周期方法, 当行动加载完成后调用

  • didMove(to view: SKView): 当场景被移到View上调用,类似于viewDidLoad

SKTransition

open class SKTransition : NSObject, NSCopying

转场效果, 类似于基本动画的transition

SKView

open class SKView : UIViewopen var ignoresSiblingOrder: Boolopen func presentScene(_ scene: SKScene, transition: SKTransition)

UIView的子类, 承载场景的View, 所有游戏的效果都在这个View中实现, 类似于导航控制器的角色

  • ignoresSiblingOrder: 是否忽略同级元素

  • presentScene: 切换场景

二、小游戏实战案例

API, 了解一些基本的就够了, 如果要深究可以打开头文件逐个尝试, 我们现在就来实现一个小游戏, 这个游戏中包含了三种角色, 僵尸, 老太和猫, 当僵尸吃到15个猫即为胜利, 僵尸被老太打到5次即为失败, 我们着手进行游戏的开发吧!

  • Step1 创建游戏后自动生成GameViewController, 将其加载自定义的场景

class GameViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()let scene =MainMenuScene(size:CGSize(width: 2048, height: 1536)) //进行主场景的设置let skView = self.view as! SKViewskView.showsFPS = true //显示帧率skView.showsNodeCount = true //显示节点个数skView.ignoresSiblingOrder = true // 忽略同级元素优先级scene.scaleMode = .aspectFill // 缩放模式设置填充适配skView.presentScene(scene) //显示场景}
}
  • Step2 注意点: 由于与UIKit坐标系不同, SpriteKit的坐标系中, 由于锚点默认为(0.5, 0.5), position的位置y轴向上取大.

class MainMenuScene: SKScene {override func didMove(to view: SKView) { //到场景被加入时调用let background = SKSpriteNode(imageNamed: "MainMenu") //设置背景图片background.position = CGPoint(x: size.width/2, y: size.height/2) /将图片设置为居中显示addChild(background) //添加到场景中}func sceneTapped() { //场景点击时调用let myScene = GameScene(size: size) //创建游戏场景myScene.scaleMode = scaleMode //赋值缩放模式let reveal = SKTransition.doorway(withDuration: 1.5) //设置转场模式及时间view?.presentScene(myScene, transition: reveal) //切换场景}override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {sceneTapped() //调用场景被点击方法}}
  • Step3 设置游戏场景的属性, 注意点, 速度包括方向和增量

class GameScene: SKScene {let zombie = SKSpriteNode(imageNamed: "zombie1") //僵尸节点var lastUpdateTime: TimeInterval = 0 //最后更新时间var dt: TimeInterval = 0 //增量let zombieMovePointsPerSec: CGFloat = 480.0 //每秒僵尸移动距离var velocity = CGPoint.zero //速度let playableRect: CGRect //可执行区域var lastTouchLocation: CGPoint? //最后触摸点let zombieRotateRadiansPerSec:CGFloat = 4.0 * π //每秒僵尸旋转的弧度let zombieAnimation: SKAction //僵尸动画let catCollisionSound: SKAction = SKAction.playSoundFileNamed( //猫音效"hitCat.wav", waitForCompletion: false)let enemyCollisionSound: SKAction = SKAction.playSoundFileNamed( //老太音效"hitCatLady.wav", waitForCompletion: false)var invincible = false //是否无敌let catMovePointsPerSec:CGFloat = 480.0 //猫每秒移动的距离var lives = 5 //设置5条命var gameOver = false //是否游戏结束let cameraNode = SKCameraNode() //摄像机let cameraMovePointsPerSec: CGFloat = 200.0 //摄像机每秒移动的距离let livesLabel = SKLabelNode(fontNamed: "Glimstick") //生命数量标签let catsLabel = SKLabelNode(fontNamed: "Glimstick") //捕获猫数量的标签var cameraRect : CGRect { //摄像头区域计算属性let x = cameraNode.position.x - size.width/2+ (size.width - playableRect.width)/2let y = cameraNode.position.y - size.height/2+ (size.height - playableRect.height)/2return CGRect(x: x,y: y,width: playableRect.width, height: playableRect.height)}...
}
  • Step4 场景的初始化方法的设置

override init(size: CGSize) {let maxAspectRatio:CGFloat = 16.0/9.0 //设置屏幕比率为 16比9let playableHeight = size.width / maxAspectRatiolet playableMargin = (size.height-playableHeight)/2.0playableRect = CGRect(x: 0, y: playableMargin, width: size.width,height: playableHeight)// 1var textures:[SKTexture] = [] //纹理数组// 2for i in 1...4 {textures.append(SKTexture(imageNamed: "zombie\(i)")) //类似imageView动画}// 3textures.append(textures[2])textures.append(textures[1])// 4zombieAnimation = SKAction.animate(with: textures,  //僵尸动画执行纹理timePerFrame: 0.1)super.init(size: size)}
  • Step5 这个没什么好说的, 就是花一条屏幕的线, 来看碰撞检测用的

func debugDrawPlayableArea() { //测试可操作区域let shape = SKShapeNode() let path = CGMutablePath()path.addRect(playableRect)shape.path = pathshape.strokeColor = SKColor.redshape.lineWidth = 4.0addChild(shape)}
  • Step6 当场景被加载的时候进行一些设置和操作

override func didMove(to view: SKView) {playBackgroundMusic(filename: "backgroundMusic.mp3") //播放背景音乐for i in 0...1 { //进行场景的设置let background = backgroundNode() //合并成一个大场景background.anchorPoint = CGPoint.zerobackground.position = CGPoint(x: CGFloat(i)*background.size.width, y: 0)background.name = "background"background.zPosition = -1 //设置为-1, 保持添加新节点永远在背景之上addChild(background)}zombie.position = CGPoint(x: 400, y: 400)  zombie.zPosition = 100addChild(zombie) //将僵尸添加入场景// zombie.run(SKAction.repeatForever(zombieAnimation))run(SKAction.repeatForever( //场景执行行动SKAction.sequence([SKAction.run() { [weak self] inself?.spawnEnemy() //添加老太},SKAction.wait(forDuration: 2.0)]))) //等待2秒run(SKAction.repeatForever(SKAction.sequence([SKAction.run() { [weak self] inself?.spawnCat() //添加猫},SKAction.wait(forDuration: 1.0)]))) //等待1秒// debugDrawPlayableArea()addChild(cameraNode) //添加摄像头节点camera = cameraNodecameraNode.position = CGPoint(x: size.width/2, y: size.height/2)livesLabel.text = "Lives: X" //命数显示的设置livesLabel.fontColor = SKColor.blacklivesLabel.fontSize = 100livesLabel.zPosition = 150livesLabel.horizontalAlignmentMode = .left //左下角livesLabel.verticalAlignmentMode = .bottomlivesLabel.position = CGPoint( x: -playableRect.size.width/2 + CGFloat(20),y: -playableRect.size.height/2 + CGFloat(20))cameraNode.addChild(livesLabel)catsLabel.text = "Cats: X" //猫数显示的设置catsLabel.fontColor = SKColor.blackcatsLabel.fontSize = 100catsLabel.zPosition = 150catsLabel.horizontalAlignmentMode = .right //右下角catsLabel.verticalAlignmentMode = .bottomcatsLabel.position = CGPoint(x: playableRect.size.width/2 - CGFloat(20),y: -playableRect.size.height/2 + CGFloat(20))cameraNode.addChild(catsLabel)}
  • Step7 背景音乐的处理

import AVFoundationvar backgroundMusicPlayer: AVAudioPlayer!func playBackgroundMusic(filename: String) { let resourceUrl = Bundle.main.url(forResource: //从沙盒中读取音乐filename, withExtension: nil)guard let url = resourceUrl else {print("Could not find file: \(filename)")return}do { try backgroundMusicPlayer = //如果路径错误或文件损坏抛出异常AVAudioPlayer(contentsOf: url)backgroundMusicPlayer.numberOfLoops = -1backgroundMusicPlayer.prepareToPlay()backgroundMusicPlayer.play()} catch {print("Could not create audio player!")return}
}
  • Step8 进行背景节点的拼接

func backgroundNode() -> SKSpriteNode {// 1let backgroundNode = SKSpriteNode()backgroundNode.anchorPoint = CGPoint.zerobackgroundNode.name = "background"// 2let background1 = SKSpriteNode(imageNamed: "background1")background1.anchorPoint = CGPoint.zerobackground1.position = CGPoint(x: 0, y: 0)backgroundNode.addChild(background1)// 3let background2 = SKSpriteNode(imageNamed: "background2") //将第二张图拼接在第一张图的后面background2.anchorPoint = CGPoint.zerobackground2.position =CGPoint(x: background1.size.width, y: 0)backgroundNode.addChild(background2)// 4backgroundNode.size = CGSize(width: background1.size.width + background2.size.width,height: background1.size.height)return backgroundNode}
  • Step9 设置老太的显示和行动

func spawnEnemy() {let enemy = SKSpriteNode(imageNamed: "enemy") //添加老太节点enemy.position = CGPoint(x: cameraRect.maxX + enemy.size.width/2,y: CGFloat.random( //随机高度出现min: cameraRect.minY + enemy.size.height/2,max: cameraRect.maxY - enemy.size.height/2))enemy.zPosition = 50enemy.name = "enemy"addChild(enemy)let actionMove = //进行老太的行动SKAction.moveBy(x: -(size.width + enemy.size.width), y: 0, duration: 2.0)let actionRemove = SKAction.removeFromParent()enemy.run(SKAction.sequence([actionMove, actionRemove])) //当移动完成后删除老太节点}
  • Step10 设置猫的显示和行动

func spawnCat() {// 1let cat = SKSpriteNode(imageNamed: "cat") //添加猫节点cat.name = "cat"cat.position = CGPoint(x: CGFloat.random(min: cameraRect.minX, //随机位置出现max: cameraRect.maxX),y: CGFloat.random(min: cameraRect.minY,max: cameraRect.maxY))cat.zPosition = 50cat.setScale(0) //初始缩放设为0addChild(cat)// 2let appear = SKAction.scale(to: 1.0, duration: 0.5) //cat.zRotation = -π / 16.0 //设置初始旋转let leftWiggle = SKAction.rotate(byAngle: π/8.0, duration: 0.5) //左边转let rightWiggle = leftWiggle.reversed() //右边转let fullWiggle = SKAction.sequence([leftWiggle, rightWiggle]) //左边转后右边转let scaleUp = SKAction.scale(by: 1.2, duration: 0.25) //进行放大let scaleDown = scaleUp.reversed() //缩小let fullScale = SKAction.sequence( //放大后缩小[scaleUp, scaleDown, scaleUp, scaleDown])let group = SKAction.group([fullScale, fullWiggle]) //同时执行缩放和旋转let groupWait = SKAction.repeat(group, count: 10) //重复10次组动画let disappear = SKAction.scale(to: 0, duration: 0.5) //缩放消失let removeFromParent = SKAction.removeFromParent() //移除节点let actions = [appear, groupWait, disappear, removeFromParent] //执行行动cat.run(SKAction.sequence(actions)) //节点执行行动}
  • Step11 随机数的设置

extension CGFloat {static func random() -> CGFloat {return CGFloat(Float(arc4random()) / Float(UInt32.max))}static func random(min: CGFloat, max: CGFloat) -> CGFloat {assert(min < max)return CGFloat.random() * (max - min) + min}
}
  • Step12 进行每帧的逻辑

override func update(_ currentTime: TimeInterval) {if lastUpdateTime > 0 { //算出每帧的增量dt = currentTime - lastUpdateTime} else {dt = 0}lastUpdateTime = currentTime/*if let lastTouchLocation = lastTouchLocation {let diff = lastTouchLocation - zombie.positionif diff.length() <= zombieMovePointsPerSec * CGFloat(dt) {zombie.position = lastTouchLocationvelocity = CGPoint.zerostopZombieAnimation()} else {*/move(sprite: zombie, velocity: velocity) //移动僵尸 基于算法rotate(sprite: zombie, direction: velocity,  //旋转僵尸 基于算法rotateRadiansPerSec: zombieRotateRadiansPerSec)/*}}*/boundsCheckZombie() //边界监测// checkCollisions()moveTrain() //捕获操作moveCamera() //移动摄像头livesLabel.text = "Lives: \(lives)" //更新命数if lives <= 0 && !gameOver { //当命用完的时候gameOver = trueprint("You lose!")backgroundMusicPlayer.stop() //停止背景音乐// 1let gameOverScene = GameOverScene(size: size, won: false)gameOverScene.scaleMode = scaleMode// 2let reveal = SKTransition.flipHorizontal(withDuration: 0.5)// 3view?.presentScene(gameOverScene, transition: reveal) //切换场景}// cameraNode.position = zombie.position}
  • Step13 僵尸移动和旋转的算法

func move(sprite: SKSpriteNode, velocity: CGPoint) {let amountToMove = CGPoint(x: velocity.x * CGFloat(dt), y: velocity.y * CGFloat(dt))sprite.position += amountToMove}func rotate(sprite: SKSpriteNode, direction: CGPoint, rotateRadiansPerSec: CGFloat) {let shortest = shortestAngleBetween(angle1: sprite.zRotation, angle2: velocity.angle)let amountToRotate = min(rotateRadiansPerSec * CGFloat(dt), abs(shortest))sprite.zRotation += shortest.sign() * amountToRotate}
  • Step14 边界检测

func boundsCheckZombie() {let bottomLeft = CGPoint(x: cameraRect.minX, y: cameraRect.minY)let topRight = CGPoint(x: cameraRect.maxX, y: cameraRect.maxY)if zombie.position.x <= bottomLeft.x {zombie.position.x = bottomLeft.xvelocity.x = abs(velocity.x)}if zombie.position.x >= topRight.x {zombie.position.x = topRight.xvelocity.x = -velocity.x}if zombie.position.y <= bottomLeft.y {zombie.position.y = bottomLeft.yvelocity.y = -velocity.y}if zombie.position.y >= topRight.y {zombie.position.y = topRight.yvelocity.y = -velocity.y} }
  • Step15 猫的捕获方法

func moveTrain() {var trainCount = 0 //设置捕获猫的数量var targetPosition = zombie.position //保存僵尸的位置enumerateChildNodes(withName: "train") { node, stop in //遍历所有猫的节点trainCount += 1 //增加猫的捕获数if !node.hasActions() { //当猫失去了行动let actionDuration = 0.3 //行动时间let offset = targetPosition - node.position //进行猫和僵尸的偏移计算let direction = offset.normalized() //转换成速度(方向加距离)let amountToMovePerSec = direction * self.catMovePointsPerSec //每秒猫移动的距离let amountToMove = amountToMovePerSec * CGFloat(actionDuration) //总共移动的距离let moveAction = SKAction.moveBy(x: amountToMove.x, y: amountToMove.y, duration: actionDuration)node.run(moveAction) //让猫移动}targetPosition = node.position //重置目标位置为之前捕获猫的位置}if trainCount >= 15 && !gameOver { //捕获超过15只gameOver = trueprint("You win!")backgroundMusicPlayer.stop()// 1let gameOverScene = GameOverScene(size: size, won: true)gameOverScene.scaleMode = scaleMode// 2let reveal = SKTransition.flipHorizontal(withDuration: 0.5)// 3view?.presentScene(gameOverScene, transition: reveal) //切换场景}catsLabel.text = "Cats: \(trainCount)" //更新捕获数}
  • Step16 移动摄像头

func moveCamera() {let backgroundVelocity =CGPoint(x: cameraMovePointsPerSec, y: 0)let amountToMove = backgroundVelocity * CGFloat(dt)cameraNode.position += amountToMove //摄像头进行移动enumerateChildNodes(withName: "background") { node, _ in //遍历背景节点let background = node as! SKSpriteNodeif background.position.x + background.size.width < self.cameraRect.origin.x { //当触碰边界 向后移动背景的位置background.position = CGPoint(x: background.position.x + background.size.width*2,y: background.position.y)}}}
  • Step17 进行屏幕的点击操作

override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?) {guard let touch = touches.first else {return}let touchLocation = touch.location(in: self)sceneTouched(touchLocation: touchLocation)}override func touchesMoved(_ touches: Set<UITouch>,with event: UIEvent?) {guard let touch = touches.first else {return}let touchLocation = touch.location(in: self)sceneTouched(touchLocation: touchLocation)}func sceneTouched(touchLocation:CGPoint) {lastTouchLocation = touchLocationmoveZombieToward(location: touchLocation)}func moveZombieToward(location: CGPoint) {startZombieAnimation()let offset = location - zombie.positionlet direction = offset.normalized()velocity = direction * zombieMovePointsPerSec //更新速度, update逐帧渲染}
  • Step18 进行僵尸动画

func startZombieAnimation() {if zombie.action(forKey: "animation") == nil {zombie.run(SKAction.repeatForever(zombieAnimation), //重复播放纹理动画withKey: "animation")}}func stopZombieAnimation() {zombie.removeAction(forKey: "animation") //删除行动}
  • Step19 碰撞检测

override func didEvaluateActions() { //update后调用checkCollisions()}func checkCollisions() {var hitCats: [SKSpriteNode] = [] //捕获到猫的数组enumerateChildNodes(withName: "cat") { node, _ in //遍历所有的猫let cat = node as! SKSpriteNodeif cat.frame.intersects(self.zombie.frame) { hitCats.append(cat) //如果和僵尸有发生碰撞, 添加入捕获数组}}for cat in hitCats {zombieHit(cat: cat) //进行捕获操作}if invincible {return //如果僵尸处以无敌状态 不检测碰撞老太}var hitEnemies: [SKSpriteNode] = [] //被老太击倒数组enumerateChildNodes(withName: "enemy") { node, _ in //遍历所有的老太let enemy = node as! SKSpriteNodeif node.frame.insetBy(dx: 20, dy: 20).intersects( //老太向内收缩触碰区域self.zombie.frame) {hitEnemies.append(enemy) //添加数组}}for enemy in hitEnemies {zombieHit(enemy: enemy) //碰撞老太}}
  • Step20 碰撞后的操作

func zombieHit(cat: SKSpriteNode) {cat.name = "train" //更改猫的标识, 标记为捕获cat.removeAllActions() //删除所有行动cat.setScale(1.0) //缩放至正常比率cat.zRotation = 0 //旋转至正常比率let turnGreen = SKAction.colorize(with: SKColor.green, colorBlendFactor: 1.0, duration: 0.2)cat.run(turnGreen) //改变颜色run(catCollisionSound) //播放音效}func zombieHit(enemy: SKSpriteNode) {invincible = true //设置为无敌状态let blinkTimes = 10.0 //闪烁时间let duration = 3.0 //无敌时间let blinkAction = SKAction.customAction(withDuration: duration) { node, elapsedTime in //let slice = duration / blinkTimeslet remainder = Double(elapsedTime).truncatingRemainder(dividingBy: slice)node.isHidden = remainder > slice / 2}let setHidden = SKAction.run() { [weak self] inself?.zombie.isHidden = falseself?.invincible = false}zombie.run(SKAction.sequence([blinkAction, setHidden])) //进行执行序列run(enemyCollisionSound) //播放音效loseCats() //丢失猫的操作lives -= 1 //失去生命值}
  • Step21 丢失猫的操作

func loseCats() {// 1var loseCount = 0 // 丢失数量enumerateChildNodes(withName: "train") { node, stop in //遍历已捕获的猫的节点// 2var randomSpot = node.position // 猫进行随机移动randomSpot.x += CGFloat.random(min: -100, max: 100)randomSpot.y += CGFloat.random(min: -100, max: 100)// 3node.name = "" //清空标识node.run(SKAction.sequence([SKAction.group([SKAction.rotate(byAngle: π*4, duration: 1.0),SKAction.move(to: randomSpot, duration: 1.0),SKAction.scale(to: 0, duration: 1.0)]),SKAction.removeFromParent()]))// 4loseCount += 1if loseCount >= 2 { //设置只丢失2只stop[0] = true}}}
  • Step22 游戏结束的场景

class GameOverScene: SKScene {  let won:Bool //是否胜利init(size: CGSize, won: Bool) {    self.won = won    super.init(size: size)}  required init(coder aDecoder: NSCoder) {    fatalError("init(coder:) has not been implemented")}  override func didMove(to view: SKView) {    var background: SKSpriteNodeif (won) {background = SKSpriteNode(imageNamed: "YouWin") //胜利的图片及音效run(SKAction.playSoundFileNamed("win.wav", waitForCompletion: false))} else {background = SKSpriteNode(imageNamed: "YouLose") //失败的图片及音效run(SKAction.playSoundFileNamed("lose.wav", waitForCompletion: false))}background.position = CGPoint(x: size.width/2, y: size.height/2)    self.addChild(background)    // More here...let wait = SKAction.wait(forDuration: 3.0) //等待3秒后重新开始游戏let block = SKAction.run {      let myScene = GameScene(size: self.size)myScene.scaleMode = self.scaleMode      let reveal = SKTransition.flipHorizontal(withDuration: 0.5)      self.view?.presentScene(myScene, transition: reveal)}    self.run(SKAction.sequence([wait, block]))}}

三、运行效果与文件截图

1、运行效果:

2、文件截图:

ZombieConga文件夹里面截图:

ZombieConga.xcodeproj文件夹里面截图:

注:本文著作权归作者,由demo大师(http://www.demodashi.com)宣传,拒绝转载,转载需要作者授权

[SpriteKit] 系统框架中Cocos2d-x制作小游戏ZombieConga相关推荐

  1. 计算机没有游戏和附件,win10系统开始菜单“附件”中不显示纸牌小游戏的操作教程...

    win10系统使用久了,好多网友反馈说win10系统开始菜单"附件"中不显示纸牌小游戏的问题,非常不方便.有什么办法可以永久解决win10系统开始菜单"附件"中 ...

  2. python游戏制作软件_python制作小游戏(二)

    下载W3Cschool手机App,0基础随时随地学编程导语 T_T突然发现N久以前我还做过一个系列??? 利用Python制作小游戏??? 好吧,既然做了,就有头有尾吧~~~ 本期我们将制作一个类似八 ...

  3. python能制作游戏吗_如何用Python制作小游戏

    要想用Python制作小游戏,必须要安装一个插件Pygame 什么是Pygame Pygame是跨平台Python模块,专为电子游戏设计,包含图像.声音.建立在SDL基础上,允许实时电子游戏研发而无需 ...

  4. 用JAVA制作小游戏——飞机大战(三)

    本篇博客是对飞机大战游戏项目完整代码的展示 详细代码讲解: 用JAVA制作小游戏--飞机大战(一) 用JAVA制作小游戏--飞机大战(二) 最下方附整个程序的文件下载链接 代码展示 主界面 impor ...

  5. 用JAVA制作小游戏——飞机大战(二)

    本篇博客是对飞机大战游戏使用代码的展示 重难点: 首先需要鼠标能够控制战机,使鼠标在窗口内时始终能够使战机的位置与鼠标相同,实现鼠标控制战斗机移动. 其次需要能够以一定的速度产生子弹和敌机,并且以一定 ...

  6. 用JAVA制作小游戏——推箱子(三)

    本篇博客主要是对推箱子地图编辑器功能的代码讲解. 首先给出这段代码的部分运行截图: 重难点: 地图编辑器主要有三个重难点: 需要有一个绘制地图的界面 能够实现地图绘制的功能 地图绘制完成后需要将地图内 ...

  7. 用JAVA制作小游戏——推箱子(二)

    本篇博客主要是推箱子游戏界面功能的代码讲解. 首先先给出这段代码的部分运行截图: 重难点: 游戏界面主要有五个重难点: 固定好地图的位置 地图的显示 构建菜单栏 读取地图数据 玩家操作功能实现 地图的 ...

  8. 如何制作小游戏(c++教程)(新手版)(1)

    如何制作小游戏1 首先,你得确定 ,自己该做什么类型以及什么内容的游戏 这里推荐新手先做 剧情类游戏 cout<<"在一个漆黑的屋子里,你发现了两个宝箱,第一个宝箱破败腐朽,第二 ...

  9. python游戏制作软件_python制作小游戏(三)

    往期回顾python制作小游戏(一)python制作小游戏(二) GitHub: https://github.com/CharlesPikachu/Games 本系列文章中所涉及到的所有源代码以及相 ...

最新文章

  1. 【C++】【四】企业链表
  2. 非静态内部类中为什么不能拥有静态成员?
  3. java环境搭建_记一次阿里云服务器Java相关环境搭建的过程
  4. linux下ip命令用法
  5. Linux下的基本常用命令解析
  6. oracle 导入导出指定表
  7. 上海名媛群事件是真实的么?
  8. C功底挑战Java菜鸟入门概念干货(三)
  9. JDY-19蓝牙模块介绍及主、从机调试演示
  10. 通过CuteFTP用VBScript使用SFTP,实现Win与Linux的文件传输
  11. 计算机审计中级题库,中级审计师用什么题库练习呢
  12. 梦想CAD软件(控件)图层介绍
  13. “蔚小理”想挑战特斯拉?先干过比亚迪再说
  14. 十分钟带你解读Effective C++(导读)
  15. android 提高启动速度慢,安卓启动速度过慢的原因及解决方法
  16. baidumap api MySQL_百度地图API
  17. 吃饭只吃5分饱,生活才会很美好
  18. 关于汇编语言中的转移指令原理——offset
  19. DxO Analyzer的基本操作
  20. eclipse的使用操作技巧

热门文章

  1. 实例对象的索引的方法
  2. enum 有什么好处_高新技术企业认定四个核心评分标准是什么?软著能加分吗?...
  3. 【嵌入式设计】嵌入式低功耗设计
  4. (9)引入哈希桶的概念来实现一个哈希表
  5. (1)散列表(哈希表)的定义
  6. K2评分方法理解实例
  7. 【LeetCode】剑指 Offer 45. 把数组排成最小的数
  8. 【Java数据结构与算法】第九章 顺序查找、二分查找、插值查找和斐波那契查找
  9. 【力扣】NO.7.整数反转
  10. 编译错误:[Error] initializer-string for array of chars is too long [-fpermissive]