iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、敵の動きを改善する。GameplayKitのPathfindingを使ったルートの自動探索の使い方についてSpriteKitのSKActionでの実装と比べて解説。
前回の「SwiftのSpriteKitで実装―タワーディフェンスゲームの大枠の作り方とコツ」では敵と味方の配置・移動と接触時の処理を実装してきました。ぼんやりとゲームの大枠が見えてきたのではないかと思います。
今回は敵の動きを改善しようと思います。今までは敵は真っすぐに移動するだけでしたが今回は道に沿って移動させます。移動経路の計算には、iOS SDK 9で登場したばかりの「GameplayKit」という最新フレームワークも利用します。
実装に入る前に本連載で作るアプリの完成形を確認しておきます。本連載では下記6つのルールを満たすタワーディフェンスを作っていきます。
今回はルール2に関する部分を実装していきます。
まずは画面に敵の通る道を表示します。キャラクターだけではなく、道の素材も「ぴぽや倉庫」のものを使わせていただきました。
最初に、こちらのFields.zipをダウンロードして、そこから「Field0.png」「Field1.png」という画像を取り出してください。
それらをXcodeのAssets.xcassetsファイルにドラッグ&ドロップすると、下図のように登録されると思います。
次は、これらの画像をフィールドに表示します。「GameScene.swift」のdidMoveToViewを以下のように書き換えてみましょう。
- class GameScene: SKScene, SKPhysicsContactDelegate {
- // …略
- override func didMoveToView(view: SKView) {
- physicsWorld.gravity = CGVectorMake(0, 0)
- physicsWorld.contactDelegate = self
- // 元々あった処理
- /*
- let fieldImageLength: CGFloat = 32
- for i in 0...Int(frame.size.width / fieldImageLength) + 1 {
- for j in 0...Int(frame.size.height / fieldImageLength) + 1 {
- let field = SKSpriteNode(imageNamed: "Field")
- field.position = CGPoint(x: CGFloat(i) * fieldImageLength, y: CGFloat(j) * fieldImageLength)
- field.zPosition = -1
- addChild(field)
- }
- }
- */
- let fieldData = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
- [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- ]
- let fieldImageLength = view.frame.width / 10
- for (i, data) in fieldData.enumerate() {
- for (j, value) in data.enumerate() {
- let field = SKSpriteNode(imageNamed: "Field\(value)")
- field.name = "Field\(value)"
- field.size = CGSize(width: fieldImageLength, height: fieldImageLength)
- field.physicsBody = SKPhysicsBody(rectangleOfSize: field.size)
- field.physicsBody?.categoryBitMask = 0x0
- field.physicsBody?.collisionBitMask = 0x0
- field.position = CGPoint(
- x: CGFloat(j) * fieldImageLength,
- y: view.frame.height - CGFloat(i - 1) * fieldImageLength)
- field.zPosition = -1
- addChild(field)
- }
- }
- // …略
- }
- // …略
- }
アプリを起動するとフィールド上に敵の通り道が表示されていることが確認できると思います。
フィールドの情報はfieldDataという2次元配列の変数を使って表すようにしました(22~43行目)。fieldDataの「0」が草むらで、「1」が道を表しています。上図の画面と22~43行目のソースコードを見比べると、「1」が道になっていることを確認できると思います。
let fieldData = [ // …略 ]
今回からはフィールドの1パネルの大きさを画面サイズによって変わるようにしました。こうすることで複数の画面サイズに対応できます。
let fieldImageLength = view.frame.width / 10
Copyright © ITmedia, Inc. All Rights Reserved.
Smart & Social 鬯ッ�ッ�ス�ョ�ス�ス�ス�ォ�ス�ス�ス�ス�ス�ス�ス�ェ鬯ョ�ッ陋ケ�コ�ス�サ郢ァ謇假スス�ス�ス�ソ�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�コ鬯ョ�」陋ケ�ス�ス�ス�ス�オ鬮ォ�エ遶擾スオ�ス�コ�ス�キ�ス�ス�ス�ク�ス�ス�ス�キ�ス�ス�ス�ス�ス�ス�ス�ケ鬮ォ�エ髮懶ス」�ス�ス�ス�「�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ウ鬯ッ�ゥ陝キ�「�ス�ス�ス�「�ス�ス�ス�ス�ス�ス�ス�ァ�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ュ鬯ッ�ゥ陝キ�「�ス�ス�ス�「鬮ォ�エ髮懶ス」�ス�ス�ス�「�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ウ鬯ッ�ゥ陝キ�「�ス�ス�ス�「�ス�ス�ス�ス�ス�ス�ス�ァ�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ス�ー