読者です 読者をやめる 読者になる 読者になる

Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 895 - GameplayKitは結構おもしろい

GameplayKitはiOS9で導入された新しいフレームワークですが、やっぱりゲームの世界は売れる市場なんで、変化と工夫が多岐に渡っていますね。iOS10でもその流れは変わらず、ネットに溢れるチュートリアルが、どれもこれもあっという間に時代遅れの情報になっています。

私がトライしている「GameplayKit with Swift for Beginners — Tower Defense Game — Part 4: Dynamic Pathfinding」も例外ではなく、少々古い内容です。GameplayKitの「Pathfinding」もiOS10でアップデイトされています。

残念なことに、私には新しいAPIの説明をする実力が備わっていないので、このままチュートリアルを進めます。

敵のキャラ(赤い四角)が画面左端に出現して右動いていきます。やぐらをタップして出現させても動きを遮ることができません。次はやぐらで行く手を遮るコードを加えていきます。自動的にルートを再計算して敵キャラの進行が変化するところがPathfindingの真骨頂です。

さて、SpriteKitとGameplayKitのPathfindingを扱う時に混乱しそうになるのが、それぞれの座標系が異なるため、自分で用意したメソッド、coordinateFor()とpointFor()を使って、int2にしたり、CGPointにしなければならないのが億劫です。

まず、createEnemies()メソッドにタワーを設置したときにPathfindingをアップデイトする仕組みを組み込む必要があります。

タワーの設置に関係なく突き進む次のコードを消去します。

// 一時的に以下のコードを加えて動作チェック
let path = movementComponent.pathToDestination()
MovementComponent.followPath(path: path)

そして、アップデイト様のメソッド「 updatePathForEntities(entities: [GKEntity])」を「createTower(atCoordinate: int2) 」の下に加えます。

func updatePathForEntities(entities: [GKEntity]) {
    for entity in entities {
        if let movementComponent = 
            entity.component(ofType: MovementComponent.self) {
            movementComponent.sprite.removeAllActions()
                
            var path = movementComponent.pathToDestination()
            path.remove(at: 0)
                
            // update visual path
                
            movementComponent.followPath(path: path)
        }
    }
}

entityがボード上に加わる度にパスを再計算させるメソッドです。アレー型のエンティティを引数として受け取ります。

エンティティにMovementComponentがあるかどうか判断して、あれば全てのActionを消去します。そして新たにpathTODestination()メソッドを使ってパスの再設定をしますが、スタート地点は現在エンティティが存在する場所になるので、インデックス「0」の位置は消去します。そしてfollowPath()メソッドを使って描画するって話しです。

// update visual path

これは明日説明します。

このメソッドをcreateEnemies()メソッド内で呼びます。

let action = SKAction.run { [unowned self] in
    let movementComponent = 
        enemy.component(ofType: MovementComponent.self)!
    self.addChild(movementComponent.sprite)
                
    self.updatePathForEntities(entities: [enemy])          
}

そしてもう1箇所、 createTower(atCoordinate: int2)メソッドの最期で呼びます。

updatePathForEntities(entities: enemies)

これで敵キャラはタワーを避けながら目的地に進みます。

f:id:yataiblue:20170105173701j:plain

次はパスを描画するステップです。