「萬事俱備,只欠東風了」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)
}
// 左右穿梭
...
}
}
目前怪物已經可以自動偵測可以前進的方向,並且隨機移動囉!
但這樣還不夠聰明~
明天將會介紹如何讓怪物朝著主角的位置移動!