iOS 9の最新機能で自動ルート検索を簡単にゲームに組み込むiOS SDKとSwiftで始めるゲーム作成入門(3)(1/3 ページ)

iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、敵の動きを改善する。GameplayKitのPathfindingを使ったルートの自動探索の使い方についてSpriteKitのSKActionでの実装と比べて解説。

» 2015年12月17日 05時00分 公開
[杉本裕樹マネーフォワード]

iOS SDK 9の「GameplayKit」を使う

「iOS SDKとSwiftで始めるゲーム作成入門」のインデックス

連載目次

 前回の「SwiftのSpriteKitで実装―タワーディフェンスゲームの大枠の作り方とコツ」では敵と味方の配置・移動と接触時の処理を実装してきました。ぼんやりとゲームの大枠が見えてきたのではないかと思います。

 今回は敵の動きを改善しようと思います。今までは敵は真っすぐに移動するだけでしたが今回は道に沿って移動させます。移動経路の計算には、iOS SDK 9で登場したばかりの「GameplayKit」という最新フレームワークも利用します。

 実装に入る前に本連載で作るアプリの完成形を確認しておきます。本連載では下記6つのルールを満たすタワーディフェンスを作っていきます。

  1. プレーヤーは開始前に与えられた所持金を元に、敵を攻撃するユニットを設置する
  2. ゲームは「Wave(ウェーブ)」という単位で行われる。基本的には1つのWaveで登場する敵の種類は1種類のみとなっている。Waveが始まると敵は入口から登場し、目的地に向かって行進する。プレーヤーが設置したユニットは攻撃可能範囲に入ると自動的に攻撃を行う
  3. 1Waveの敵を全て全滅させるとWaveクリアとなる。プレーヤーは次のWaveが始まるまでにユニットの増強(新設・アップグレード・売却など)を行う
  4. 以上の2と3を繰り返して行う
  5. 敵が目的地に到達すると、自分が所持しているライフが減少する。全て失うとゲームオーバーとなる
  6. ライフを全て失う前に、最終Waveの敵を全て全滅させることができればクリアとなる

 今回はルール2に関する部分を実装していきます。

前回までに実装したアプリの画面(再掲)

敵の通る道を2次元配列で作る

 まずは画面に敵の通る道を表示します。キャラクターだけではなく、道の素材も「ぴぽや倉庫」のものを使わせていただきました。

 最初に、こちらのFields.zipをダウンロードして、そこから「Field0.png」「Field1.png」という画像を取り出してください。

Fields.zipの中身

 それらを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)
            }
        }
        // …略
    }
    // …略
}

 アプリを起動するとフィールド上に敵の通り道が表示されていることが確認できると思います。

「0」が草むら、「1」が道の2次元配列

 フィールドの情報はfieldDataという2次元配列の変数を使って表すようにしました(22〜43行目)。fieldDataの「0」が草むらで、「1」が道を表しています。上図の画面と22〜43行目のソースコードを見比べると、「1」が道になっていることを確認できると思います。

let fieldData = [
    // …略
]

フィールドの1パネルの大きさを画面サイズに合わせる

 今回からはフィールドの1パネルの大きさを画面サイズによって変わるようにしました。こうすることで複数の画面サイズに対応できます。

let fieldImageLength = view.frame.width / 10
       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。