天色突然暗了下来,一股诡谲感弥漫,令人不禁冒出冷汗。
还好,随身携带头灯可是探险家的必备要领。
山姆把头灯戴上,整座黑森林里只看得见山姆一个人。
「必须下山了!终点应该快到了吧!」
PS. 这里是开发 iOS 手机游戏的系列文,如果还没看过之前
剧情文章的朋友,欢迎先点这边回顾唷!
我们一样将侦测的逻辑写在 update
里,将 crystals
及 magicCrystals
使用 filter
过滤出还没有被收集的 水晶/魔幻水晶
,并使用 isComplete
参数来判断是否已经进入破关状态,接着执行破关方法: gameComplete
gameComplete
中写入要做的动作:
stopTimer()
:将所有计时器关闭setCanMove(isCanMove: false)
:让主角及怪物设定为不能移动playMusicByName
:播放游戏破关音乐 FinishMusic
,约 6 秒gameNextLevel
gameNextLevel
:
resetPosition
:主角及怪物设定回原来的位置setMode(mode: .ATTACK)
:将怪物的模式设定回攻击setGotten(isGotten:false)
:收集物皆设定回尚未收集的状态level
加 1isComplete
设定回 false
gameStart
方法,让游戏开始class GameScene: SKScene {
...
var isComplete: Bool = false
var level: Int = 1
override func update(_ currentTime: TimeInterval) {
...
if self.crystals.filter({!$0.isGotten}).count == 0 && self.magicCrystals.filter({!$0.isGotten}).count == 0 && !self.isComplete {
self.isComplete = true
self.gameComplete()
}
}
func gameComplete() {
self.stopTimer()
if let sam = self.sam {
sam.setCanMove(isCanMove: false)
}
for weather in weathers {
weather.setCanMove(isCanMove: false)
}
self.playMusicByName(musicName: "FinishMusic")
Timer.scheduledTimer(timeInterval: 6, target: self, selector: #selector(gameNextLevel), userInfo: nil, repeats: false)
}
@objc func gameNextLevel() {
for weather in self.weathers {
weather.resetPosition()
weather.setMode(mode: .ATTACK)
}
if let sam = self.sam {
sam.resetPosition()
}
for crystal in self.crystals {
crystal.setGotten(isGotten:false)
}
for magicCrystal in self.magicCrystals {
magicCrystal.setGotten(isGotten:false)
}
for mushroom in self.mushrooms {
mushroom.setGotten(isGotten:false)
}
self.level += 1
self.isComplete = false
self.gameStart()
}
}
levelLabel
,用来显示当前的等级,显示文字设定为 Level: \(self.level)
。将文字的颜色 (fontColor)、大小 (fontSize)、字体 (fontName)、垂直对齐 (verticalAlignmentMode)、水平对齐 (horizontalAlignmentMode) 分别设定好,将等级文字节点加到场景中applySafeArea
方法中,校正等级文字节点的位置,方法跟之前使用的一样gameNextLevel
方法,进入下一关时,更新等级的文字显示class GameScene: SKScene {
...
var levelLabel: SKLabelNode?
override func didMove(to view: SKView) {
...
self.levelLabel = SKLabelNode(text: "Level: \(self.level)")
if let levelLabel = self.levelLabel {
levelLabel.fontColor = UIColor.white
levelLabel.fontSize = CGFloat(22)
levelLabel.fontName = "Copperplate"
levelLabel.verticalAlignmentMode = .center
levelLabel.horizontalAlignmentMode = .right
self.addChild(levelLabel)
}
}
func applySafeArea() {
...
if let mapNode = self.mapNode, let scoreNode = self.scoreNode, let lifeNode = self.lifeNode, let levelLabel = self.levelLabel {
mapNode.position = CGPoint(x: 0, y: -self.topSafeArea - scoreNode.size.height)
scoreNode.position = CGPoint(x: 0 ,y: -self.topSafeArea - scoreNode.size.height/2)
lifeNode.position = CGPoint(x: 0, y: -self.topSafeArea - scoreNode.size.height - mapNode.size.height - 15)
levelLabel.position = CGPoint(x: self.size.width - 10, y: -self.topSafeArea - scoreNode.size.height - mapNode.size.height - 15)
}
}
@objc func gameNextLevel() {
...
self.levelLabel!.text = "Level: \(self.level)"
}
}
clearLabel
,放在画面的中央,文字设定为 Clear!
alpha
为 0
,先暂时不显示mapNode
中5
次class GameScene: SKScene {
...
var clearLabel: SKLabelNode?
override func didMove(to view: SKView) {
...
self.clearLabel = SKLabelNode(text: "Clear!")
if let clearLabel = self.clearLabel {
clearLabel.fontColor = UIColor.white
clearLabel.fontSize = CGFloat(22)
clearLabel.fontName = "Copperplate"
clearLabel.position = CGPoint(x: self.gridWH * 8 + gridWH/2, y: -gridWH * 12 - gridWH/2);
clearLabel.verticalAlignmentMode = .center
clearLabel.horizontalAlignmentMode = .center
clearLabel.zPosition = 5
clearLabel.alpha = 0
self.mapNode!.addChild(clearLabel)
}
}
func gameComplete() {
...
let ani1 = SKAction.fadeAlpha(to: 1, duration: 0.6)
let ani2 = SKAction.fadeAlpha(to: 0, duration: 0.3)
let aniAlpha = SKAction.sequence([ani1, ani2])
let aniRepeat = SKAction.repeat(aniAlpha, count: 5)
self.clearLabel!.run(aniRepeat)
}
}
破关後,游戏中间会出现 Clear! 的文字,接着游戏重新设定,并且可以看到右下角的等级上升。
但目前破关後没有任何的改变,我们接着来为它增加下一关的难度。
我们先制作第 2 关以後的游戏场景,都具有聚光灯的效果
可以想像成游戏画面会变成黑色,只有放上光节点的地方会被照亮,我们让这个光源跟着主角一起移动,制造出像是在主角头上打了一盏聚光灯的感觉
使用 SKLightNode 类别可以新增一个灯光实体,用来照亮附近的节点。
有三种颜色属性可以设定:
接着我们来做设定:
addLight()
light
ambientColor
及 lightColor
的颜色categoryBitMask
设定为 1
,设定灯光的类型为 1
falloff
:光源的衰减率指数,设定为 1
class GameScene: SKScene {
...
var light: SKLightNode?
func addLight() {
if self.light == nil {
self.light = SKLightNode()
self.light!.ambientColor = UIColor(red: 50/255, green: 50/255, blue: 50/255, alpha: 0.3)
self.light!.lightColor = UIColor(red: 250/255, green: 250/255, blue: 250/255, alpha: 0.8)
self.light!.categoryBitMask = 1
self.light!.falloff = 1
self.mapNode!.addChild(self.light!)
}
}
}
我们希望迷宫中的所有东西,包含主角、怪物、地图、水晶等收集物都能有光照的效果,因此可以透过设定节点的 lightingBitMask
的值,让它跟灯光的 categoryBitMask
属性值一样,就可以产生被光源照射的效果
lightingBitMask
值设定为 1
class GameScene: SKScene {
...
override func didMove(to view: SKView) {
...
self.sam!.node.lightingBitMask = 1
rain.node.lightingBitMask = 1
storm.node.lightingBitMask = 1
lightning.node.lightingBitMask = 1
snow.node.lightingBitMask = 1
}
func drawMap() {
for i in 0..<gridYCount {
let mapRowArr = Array(mapDraw[i]);
for j in 0..<gridXCount {
let mapKeys = wallMapping.keys
switch mapRowArr[j] {
case _ where mapKeys.contains(mapRowArr[j]):
let spriteItem = SKSpriteNode(imageNamed: wallMapping[mapRowArr[j]]!)
...
spriteItem.lightingBitMask = 1
case "+":
let mushroom = Collection(gridWH: self.gridWH, gridX: j, gridY: i, imageName: "mushroom")
...
mushroom.node.lightingBitMask = 1
case ".":
let crystal = Collection(gridWH: self.gridWH, gridX: j, gridY: i, imageName: "crystal")
...
crystal.node.lightingBitMask = 1
case "*":
let magicalCrystal = MagicalCrystal(gridWH: self.gridWH, gridX: j, gridY: i, imageName: "magical-crystal")
...
magicalCrystal.node.lightingBitMask = 1
default:
break
}
}
}
}
}
写好新增灯光节点 (SKLightNode) 的方法後,我们在游戏进入下一关的时候呼叫它: addLight()
class GameScene: SKScene {
...
@objc func gameNextLevel() {
self.addLight()
...
}
}
为了让灯光能跟着主角移动,在 update
中,判断游戏如果已经有新增灯光节点的话,就改动它的位置 position
,让它跟主角的位置一样
class GameScene: SKScene {
...
override func update(_ currentTime: TimeInterval) {
...
if let light = self.light {
light.position = sam.node.position
}
}
}
在游戏结束时,将灯光节点移除 removeChildren(in: [light])
class GameScene: SKScene {
...
@objc func gameOver() {
...
if let light = self.light {
self.mapNode!.removeChildren(in: [light])
}
...
}
}
可以看到游戏破关後,开始有场景灯光效果,也会跟着主角移动
聚光灯效果
只有主角附近有灯光,其他会随着范围越远而看不到,更有黑森林的感觉了!
大家可以试着优化,将玩家的最高关卡等级也纪录到本机中,并且显示在游戏结束的画面上。
这边因为篇幅有限的关系,只介绍第 2 关以後的关卡加上聚光灯效果来提升难度,大家可以再继续让後面关卡的地图,样子长得跟之前的关卡都不一样,增加多样性。
参考来源:
repeat(_:count:)
SKLightNode
ambientColor
lightColor
shadowColor
categoryBitMask
falloff
lightingBitMask
除了有BETWEEN AND 之外,还有NOT BETWEEN AND (NOT BETWEEN A...
实作 新增依赖 <!-- JWT --> <dependency> <...
Point wise feed forward network 在两层全连阶层中加入一个relu的激...
大家好! 今天要介绍的是能控制网页内容的原型方法。 我们进入今天的主题吧! 插入元素 Felix.p...
Swift 语法介绍 枚举(Enumerations) ,类和结构体 枚举(Enumerations...