正當山姆思考結界問題的同時,啪嗒!啪嗒!雨落了下來。
「下雨了!」山姆趕緊找尋遮蔽物,跑到了一棵大樹下。
「呼,好險。」山姆喘了一口氣,還好背包沒有濕掉。
山姆蹲下繫緊因奔跑而鬆脫的鞋帶,抬起頭時,出現了一雙眼睛緊盯著。
四目交對之下,山姆快速掃遍腦中的資料庫...
「那是...雪人?這個季節裡居然有雪人!?」
PS. 這裡是開發 iOS 手機遊戲的系列文,如果還沒看過之前
劇情文章的朋友,歡迎先點這邊回顧唷!
依照我們的遊戲設計規劃,怪物共有四種:
以下先設計好怪物的樣子,每種怪物都先分別設計兩張動畫序列圖,我們先製作一種方向 (往下走的正面),並將圖片拖移進專案中
下雨 (Rain):

 
暴風雨 (Storm):

 
閃電 (Lightning):

 
雪人 (Snow):

 
先前已經定義過主角的一些設定值,我們先將怪物的設定值也加進專案中
在之前主角篇新增的 Role 列舉,再多加入四種怪物名稱 RAIN、STORM、LIGHTNING、SNOW
enum Role: String {
    case NONE = "none"
    case SAM = "sam"
    case RAIN = "rain"
    case STORM = "storm"
    case LIGHTNING = "lightning"
    case SNOW = "snow"
}
將怪物在迷宮中的起始位置定義在之前宣告的 gridMapping 結構 (struct) 中,方便一起管理
struct gridMapping {
    ...
    struct rainStart {
        static let x = 7
        static let y = 8
    }
    struct stormStart {
        static let x = 8
        static let y = 10
    }
    struct lightningStart {
        static let x = 6
        static let y = 10
    }
    struct snowStart {
        static let x = 9
        static let y = 8
    }
}
接著,請新增一個 swift 檔案,命名為 Weather.swift
點選新增檔案
選擇 Swift File -> Next
將檔案命名為 Weather,點擊 Create 按鈕,完成 .swift 檔的新增
請先 import SpriteKit
import SpriteKit
在這邊我們先定義好怪物將會進行的移動模式種類,將會在未來的章節實做移動的方式
enum Mode {
    case ATTACK
    case ESCAPE
    case PLAY
    case REBIRTH
}
將怪物類別 (Weather) 繼承角色類別 (GameCharacter)
接著加上怪物專有的一些屬性:
.ATTACK
true
接著在建構子 (init) 寫上初始化時需帶入的參數:
在 super.init 的時候帶入的參數:
"\(role.rawValue)_down",對應到前面加入的圖片檔名,例如 rain_down。在 GameCharacter 類別中,先前已經在 init 寫好播放動畫序列圖的程式碼,帶入圖檔名就可以產生序列動畫了ZPosition.WEATHER,在先前的章節已經加好的怪物層級並且將 sam 主角的資料存起來
class Weather: GameCharacter {
    var mode: Mode = .ATTACK
    var isTrace: Bool = true
    var targetGridX = 0
    var targetGridY = 0
    var sam: Sam?
    
    init(gridWH: Int, startGridX: Int, startGridY: Int, role: Role, sam: Sam) {
        super.init(gridWH: gridWH, startGridX: startGridX, startGridY: startGridY, imageName: "\(role.rawValue)_down", zPosition: CGFloat(ZPosition.WEATHER), role: role)
        self.sam = sam
    }
}
回到 GameScene,我們新增怪物的實體,將怪物新增到遊戲中
weathers,準備儲存所有的怪物didMove 裡分別新增四個怪物:rain、storm、lightning、snow,並將它們的 node 加到 mapNode 裡面,以及將實體加進 weathers 陣列中class GameScene: SKScene {
    ...
    var weathers: [Weather] = []
    
    override func didMove(to view: SKView) {
        ...
        let rain = Weather(gridWH: self.gridWH, startGridX: gridMapping.rainStart.x, startGridY: gridMapping.rainStart.y, role: .RAIN, sam: self.sam!)
        self.mapNode!.addChild(rain.node)
        self.weathers.append(rain)
        
        let storm = Weather(gridWH: self.gridWH, startGridX: gridMapping.snowStart.x, startGridY: gridMapping.snowStart.y, role: .SNOW, sam: self.sam!)
        self.mapNode!.addChild(storm.node)
        self.weathers.append(storm)
        let lightning = Weather(gridWH: self.gridWH, startGridX: gridMapping.lightningStart.x, startGridY: gridMapping.lightningStart.y, role: .LIGHTNING, sam: self.sam!)
        self.mapNode!.addChild(lightning.node)
        self.weathers.append(lightning)
        let snow = Weather(gridWH: self.gridWH, startGridX: gridMapping.stormStart.x, startGridY: gridMapping.stormStart.y, role: .STORM, sam: self.sam!)
        self.mapNode!.addChild(snow.node)
        self.weathers.append(snow)
    }
}
來看一下執行的成果吧!
目前我們有主角 (Sam) 及怪物 (Weather) 兩種類別了,同樣都繼承角色 (GameCharacter) 父類別,而透過繼承類別的好處是省略了一些重複的程式碼,像是初始化的地方,已經可以不用再多寫一次播放序列動畫的程式碼,並且也能依照特殊需求,再多加入特別的參數,使我們能更快速建立不同種類的角色。
聰明的各位應該也猜到了,接下來一樣可以遵循前面建立好的移動協定 Move,明日就來帶著大家一起實作讓怪物動起來吧!