ゲーム用フレームワークであるGameplayKitを使ったアプリ制作を通して人工知能(AI)について学んでいく連載。最終回は、3Dゲーム用のフレームワークSceneKitを使った簡単なアプリ制作を通して、3Dゲーム用のAIについて学ぶ。
本連載『ゲームの「敵」キャラで分かる「人工知能」の作り方』では、ゲーム用フレームワークであるGameplayKitを使ったアプリ制作を通して人工知能(AI)について学んでいきます。
前回の「iOS GameplayKitのRule SystemsでゲームAIプログラミングはどう変わるのか」までは、iPhone向け鬼ごっこアプリ制作を通して2DゲームにおけるAIの実装を行いました。
連載最終回となる本稿では、簡単な3Dアプリ制作を通して、3Dゲーム用のAIについて学びます。
今回はオブジェクトが目的地に向かって移動するアプリを作っていきます。
まずは3Dゲームのプロジェクトを作成します。Xcodeで新規プロジェクト作成を選択してください。
テンプレート選択画面では「Game」を選択します。
最後に、アプリの詳細を入力しますが、ここでは「SceneKit」を選択します。
これでプロジェクトを作成できました。起動すると、飛行機が回っている画面が表示されるかと思います。
SceneKitとは、3Dゲーム用のフレームワークで、3Dゲームを作るためのさまざまな機能を提供します。
サンプルのソースコードを基に、SceneKitの機能について簡単に見ていきます。
SceneKitでは「SCNScene」が1つ1つの画面に相当します。こちらはSpriteKitにおけるSKSceneに相当します。この上に飛行機などのオブジェクト(SCNNode)を配置することでゲームを作っていきます。
SCNSceneはUIViewControllerの「SCNView」にセットすることで画面に表示されます。サンプルアプリでは、下記のように「ship.scn」からSCNSceneを読み込んでSCNViewにセットしています。
class GameViewController: UIViewController { override func viewDidLoad() { let scene = SCNScene(named: "art.scnassets/ship.scn")! // 省略 let scnView = self.view as! SCNView scnView.scene = scene // 省略 } // 省略 }
画面タップの検知はSpriteKitと少し違うので、注意が必要です。SpriteKitのSKSceneはUIResponderのサブクラスのため、以下のようにタップを検知できました。
// SpriteKitでは以下のようにタップを検知 class MyScene: SKScene { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { } }
SCNSceneはUIResponderのサブクラスではないため、SCNViewにUITapGestureRecognizerを追加する必要があります。
サンプルでは、以下のように実装しています。
class GameViewController: UIViewController { override func viewDidLoad() { // 省略 let scnView = self.view as! SCNView // 省略 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) scnView.addGestureRecognizer(tapGesture) } func handleTap(_ gestureRecognize: UIGestureRecognizer) { // 省略 } // 省略 }
毎フレーム呼ばれるメソッドもSpriteKitと違いがあります。SpriteKitではSKSceneのupdateメソッドが毎フレーム呼ばれていました。しかしSceneKitのSCNSceneにはupdateメソッドがないため、他の方法を採る必要があります。
class MyScene: SKScene { override func update(_ currentTime: TimeInterval) { } }
方法としては、「SCNSceneRendererDelegate」を使うものがあります。以下のようにSCNSceneRendererDelegateの「renderer」を使うと、毎フレーム呼ばれるメソッドを実装できます。
class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let scnView = view as? SCNView scnView?.delegate = self } } extension GameViewController: SCNSceneRendererDelegate { func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { // 1フレームごとに呼ばれるメソッド } }
オブジェクトのアニメーションには「SCNAction」を使います。
サンプルでは、以下のように飛行機に回転アニメーションを追加しています。こちらはSpriteKitの「SKAction」とほぼ同じですが、扱う位置情報が2次元から3次元になっています。
let ship = scene.rootNode.childNode(withName: "ship", recursively: true)! ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
Copyright © ITmedia, Inc. All Rights Reserved.