「万事俱备,只欠东风了」Rain 对着 Storm 说。
「居然有人敢入侵我们的家园!」Lightning 说到激动处还不时冒出电流。
「我看这家伙走路有点慢,我们快去把他赶走吧!」Snow 回忆着撞见山姆时的情境。
午後的黑森林,要变天了。
PS. 这里是开发 iOS 手机游戏的系列文,如果还没看过之前
剧情文章的朋友,欢迎先点这边回顾唷!
先复习一下,目前我们的主角-山姆,已经可以在迷宫中移动了,想更详细了解,可以点击:【Level 14】让主角奔跑吧!Running Sam
我们已经在共用的角色类别 GameCharacter
写好可以帮助制作移动跟动画的有:
Direction
getValidDirection()
isWall(dir: Direction)
Move
playAnimation(imageName: String, num: Int, repeatAni: Bool = true)
回到我们的坏天气怪物类别 (Weather),让它遵循 Move protocol,并且实作方法
Move
,不过移动方向由方法内决定,可以忽略带进来的 direction
参数getValidDirection()
取得当前可以前进的方向 validDirections
randomElement()
取得任一个随机的可行方向,因得到的结果是 Optional 型别,所以这边运用了 guard
的功能,当没有取得时直接 returnself.direction
isMoving
是否正在移动的值SKAction.moveBy
播放移动动画,x
及 y
带入移动的向量,duration
带入移动间隔秒数。最後透过 .run
执行动画,当动画完成时,执行 endMove
方法startMove
方法,会让移动动画持续进行class Weather: GameCharacter, Move {
...
func startMove(direction : Direction) {
// 取得可行方向
let validDirections: [Direction] = self.getValidDirection()
// 随机取得一个可行方向
guard let randomDirection = validDirections.randomElement() else {
return
}
self.direction = randomDirection
// 是否正在移动中
self.isMoving = self.direction != Direction.NONE
// 播放移动动画
let animation = SKAction.moveBy(x: self.moveX[self.direction]!, y: self.moveY[self.direction]!, duration: self.moveInterval)
self.node.run(animation, completion: endMove)
// 设定格子
self.setGridXY(direction: self.direction)
}
func endMove() {
self.startMove(direction: self.direction)
}
}
在游戏场景的 didMove
,建立好怪物之後,让每一个怪物都呼叫移动方法 startMove
就可以看到怪物在地图内自动移动了
class GameScene: SKScene {
...
override func didMove(to view: SKView) {
...
for weather in self.weathers {
weather.startMove(direction: .NONE)
}
}
}
目前怪物可以随机移动了,可是看起来有一直来回走动的感觉
为了修正这个问题,我们希望能让怪物一直往前走,或者是转个弯再走,避开回头走的方向
所以在取得方向的时候,稍微修正一下:
newDirection
,先暂时将随机取得的方向存进变数中newDirection
相反self.direction
class Weather: GameCharacter, Move {
...
func startMove(direction : Direction) {
var newDirection: Direction = .NONE
// 取得可行方向
let validDirections: [Direction] = self.getValidDirection()
// 随机取得一个可行方向
guard let randomDirection = validDirections.randomElement() else {
return
}
newDirection = randomDirection
// 判断是否回头走
let isBack = self.direction == .LEFT && newDirection == .RIGHT ||
self.direction == .RIGHT && newDirection == .LEFT ||
self.direction == .UP && newDirection == .DOWN ||
self.direction == .DOWN && newDirection == .UP
// 不是回头路 或 原方向撞墙,才更新方向
if !isBack || !validDirections.contains(self.direction) {
self.direction = newDirection
}
...
}
}
最後,让怪物也能透过左右的通道穿梭地图吧!
这边的方式跟制作主角移动时一样
在播放移动动画之前,先判断走到特地位置时,就将怪物瞬间移动到地图另一边
class Weather: GameCharacter, Move {
...
func startMove(direction : Direction) {
...
// 左右穿梭
if ((gridX == gridMapping.leftPass1.x && gridY == gridMapping.leftPass1.y && direction == .LEFT) || (gridX == gridMapping.leftPass2.x && gridY == gridMapping.leftPass2.y && direction == .LEFT) || (gridX == gridMapping.rightPass1.x && gridY == gridMapping.rightPass1.y && direction == .RIGHT)) {
self.gridX = direction == .LEFT ? gridMapping.rightPass1.x + 1 : gridMapping.leftPass1.x - 1
self.gridY = direction == .LEFT ? gridMapping.rightPass1.y : [gridMapping.leftPass1.y, gridMapping.leftPass2.y].randomElement()!
self.node.position = CGPoint(x: (gridX * gridWH) + (gridWH/2), y: -gridY * gridWH - (gridWH/2))
}
// 播放移动动画
let animation = SKAction.moveBy(x: self.moveX[self.direction]!, y: self.moveY[self.direction]!, duration: self.moveInterval)
self.node.run(animation, completion: endMove)
...
}
}
目前的怪物只有正面一个方向的序列图,我们将他优化,把各个方向都做出来吧!
因为怪物有不同种类的移动模式,我们预计在不同模式会显示不同的样貌
因此在加各方向的图片时,我们事先规划,在 Mode
加上 getImage
来取得对应移动模式之下,对应怪物的图片名称
目前我们先以预设 .ATTACK
模式来制作,其他模式未来会再说明
enum Mode {
case ATTACK // 追击主角
case ESCAPE // 逃离主角
case PLAY // 聚集到湖边玩耍
case REBIRTH // 回到出生点重生
func getImage(role: Role) -> String {
switch self {
case .ATTACK, .PLAY:
switch role {
case .RAIN:
return "rain"
case .STORM:
return "storm"
case .LIGHTNING:
return "lightning"
case .SNOW:
return "snow"
default:
return ""
}
case .ESCAPE:
return "cloud"
case .REBIRTH:
return "water"
}
}
}
秘技:这边比较特别的是运用了在列举 (enum) 里使用方法 (function) 的技巧,可以结合 switch case,分别让各个 case 回传不同的结果,只需要加上
switch self
,就可以方便地对自身的 case 做条件判断并回传对应的值。
最後,来写播放动画的程序码吧!
startMove
方法後,就先将当下的移动方向暂存到 tempDirection
playAnimation
imageName
用其属性来组成:
self.mode.getImage(role: self.role)
,取得怪物种类名称self.direction.rawValue
,取得当前方向rain_left
num
:2张序列图class Weather: GameCharacter, Move {
...
func startMove(direction : Direction) {
let tempDirection: Direction = self.direction
...
self.isMoving = self.direction != Direction.NONE
// 改变怪物图片
if tempDirection != self.direction {
self.playAnimation(imageName: "\(self.mode.getImage(role: self.role))_\(self.direction.rawValue)", num: 2)
}
// 左右穿梭
...
}
}
目前怪物已经可以自动侦测可以前进的方向,并且随机移动罗!
但这样还不够聪明~
明天将会介绍如何让怪物朝着主角的位置移动!
<<: Day 12. 生命周期 - Lifecycle Hooks
PHP函数 函数返回值return 值通过使用可选的返回语句返回。可以返回包括数组和对象的任意类型。...
在开始画设计稿之前,我们要先确认一下我们的字级以及主色以及辅色等等。这边我套用去年自制的 Desig...
微软也做游戏机!这种在现在听来理所当然的事情、发生在 20 年前、其实还是让人感到挺震惊的。而当时诞...
注:本文同步刊载在Medium,若习惯Medium的话亦可去那边看呦! 今天我们要来介绍的是Matp...
出於书本 Chapter 8. Network Infrastructure SNMP 扫描 什麽是...