ARKitの練習がてら、知育アプリみたいなのを作ってみました
昨年リリースされた「ポケモンGO」はいまだ老若男女に根強い人気で、今でも街なかでチラホラとスマホ片手にボールを投げ(指シュッ)てる人をよく見かけます。
ポケモンGOはまさにAR(Augmented Reality)機能をメインに利用したアプリであり、「AR?なんじゃそりゃ」と言う人はもちろんまだまだ大勢いらっしゃるでしょうがなんとなく「AR」という言葉を耳にする機会が一般の方にも多くなったんじゃないでしょうか。
今後ますますAR技術は加速していくでしょうし、端くれとはいえスマートフォンアプリ開発で飯を食べている自分としては単純な技術への好奇心と、どんどん進化する技術に取り残される危機感半々な気持ちでARKitを使って手始めに簡単なアプリを作ってみようとしたのが今回の本題となります。
A:一応、目的を持って作る
ただ一画面に画像を表示させて「おー、写った写った」、で終わるのはあまりにも短絡的というか、自分の性格上、コイツここからさらに進んで覚えていく気があるのか?という気持ちもするので、一応簡易なものでも「こういうアプリです」と説明できるようなアプリにしようと思いました。
ただ、今回の学習は一応時間制限を設けているので(設けないとやらない)あんまり凝ると途中放棄になりかねないので、ある程度の規模に抑えます。
それでまず思い浮かんだのが、「小さい子供にとりあえず暇つぶしに遊んでもらえそうなもの」というものだったので、そんな具合のものを作ってみました。
B:作るものの概要
・画面は2つ(選択画面とAR表示画面)
・操作は全部タップのみ
・全部ひらがな
・一応デザインは子供向けにする
・犬
という具合です。それでは実際に開発をスタートしていきます。
①:新規でプロジェクトを作成する。
まずは今回のアプリのプロジェクトを作成します。Xcode9であればARの開発に必要なものはすべて揃っているので、cocoapodsなどで外部から新しいライブラリを追加する必要はありません。
なので「single view application」でサクッとまずはプロジェクトを作ってしまいます。そして今回の構成は
・画面は2つ(選択画面とAR表示画面)
という形式なので、ViewControllerを2つ使います。初期で用意されているのは一つなので、新しいViewControllerを1つ作成します。名前はそれぞれ
・StartViewController(起動画面兼、選択画面)
・ARViewController(AR表示画面)
という形にしました。プロジェクトが大規模で複雑なものであればもっと用途に忠実に沿った名称が好ましいでしょうがこの規模であればこれくらい直感的でもいいかと思います。
②:下準備
StartViewControllerに関しては、画像やARオブジェクトの効果の審議判定などを選択させるだけなので特別なライブラリを入れる必要はありません。ただし今回のメインであるARViewControllerに関しては、もちろん特別なライブラリを入れる必要があるため、こちらには必要なライブラリをimportします。
今回のメインである「ARKit」、表示オブジェクトの描画に必要な「SceneKit」を読み込ませます。そしてARViewに必要なプロトコルとして「ARSCNViewDelegate」を付け加えます。これで下準備はとりあえず完了です。ここで何かエラーが出た場合には、Xcodeのバージョンやらこのプロジェクトの対象iOSバージョンが最低11以上じゃないなど、諸々のバージョンが不適切な能性があります。
また、スクリーンボード側には「ARSCNView」を配置します。全画面に出すのでここはそのままストーリボード全体に覆わせて配置し、ARViewControllerと紐づけます。controller内では「sceneView」と名付けてます。
また、生成する画像AR情報を格納するSNSceneも定数として定義します。
わかりやすいように定数名はsceneとします。
その他、変数としてあらかじめ選択画面となるStartViewControllerから受けとる諸々の値を作成しておきます。「表示する画像名称」「表示サイズ」「丸くする」「回転アニメーションを入れる」などのフラグなども受け取ります。
画面起動時に処理されるviewDidLoadにARに関する必要コードを追記します。内容としてはsupre.viewDidLoad()の他に
self.setNeedsStatusBarAppearanceUpdate()
sceneView.delegate = self
sceneView.showsStatistics = true
の3つを追記するだけで大丈夫です。
(画像内の以下の項目は表示するARのレイアウトを受け取ったフラグで値をいじっている箇所です。もうちょっときれいにまとめたかったですがそれは追々.......)
また、viewWillApprearには、
let configuration = ARWorldTrackingConfiguration()
sceneView.session.run(configuration)
viewWillDisaooearには
sceneView.session.pause()
をそれぞれ追記します。これでいったんこのcontroller内でARを表示する下準備が完成しました。あとは、最初に作成したsceneに表示したい情報を盛り込んでsceneViewに入れてあげるだけとなります。
が、その情報を盛り込むのもまた色々あるので見ていきましょう。
③:ARマテリアルを作成する関数
今回の実装が、最初のstartViewControllerで選んだ犬をこのAR画面で表示する、という実装なのでとりあえず画像名と座標指定すれば汎用的にsceneを作成できる関数を作りました。
func createScene(imageName:String, x:Float, y:Float) {
引数はassetsに登録してある画像名、x座標、y座標です。
let image = SCNMaterial()
image.diffuse.contents = UIImage(named: imageName)
次に画像(UIImage)をSCN用のマテリアルに指定してあげます。
let cube = SCNBox(width: self.scaleSize, height: self.scaleSize, length: self.lengthSize, chamferRadius: self.sphereSize)
let node = SCNNode(geometry: cube)
let material = SCNMaterial()
次に上のimageを入れる箱(SCNBox)のようなものを作ります。
width、height、length、chamferRadius(丸み)を指定してあげます。
選択画面で「大きくするか小さくするか」「丸くするかしないか」だけ
選ばせるので、ここでは大きい場合や小さい場合の値をviewDidloadで決めてここで入れて上げている形になります。
if self.rotateFlag {
let rotate = CABasicAnimation(keyPath: "rotation")
rotate.fromValue = SCNVector4(0.0, 0.0, 0.0, 0.0)
rotate.toValue = SCNVector4(0.0, 1.0, 0.0, Float.pi * 2.0)
rotate.duration = 3
rotate.repeatCount = HUGE
node.addAnimation(rotate, forKey: "rotate")
}
ここは選択画面で対象を回転させるという選択した場合にのみ処理されます。先述のnodeにアニメーションを指定してあげる感じです。
material.diffuse.contents = UIColor.white
node.geometry?.firstMaterial = material
node.position = SCNVector3Make(x, y, -0.6);
cube.materials = [image]
self.scene.rootNode.addChildNode(node)
nodeのpositionに引数のxとyを指定してあげます、第三引数のzは今回は固定で入れてしまっていますが、「近づける遠ざける」の様な設定を作ってあげてここも引数から代入のように作ってしまうのもいいかもしれないですが今回は割愛(社内発表まで時間がないので......)
最後に、node内のcubeに最初に作成したimageを入れてあげて、nodeをsceneに突っ込みます。これでこの関数の準備はとりあえず完了です。
あとはこの関数を呼び出してあげるだけですが、一応変な機能も入れたかったので全犬を画面に表示する機能も作りました。
なので、最初に画面を表示する際に全犬表示か、指定した犬を表示するかの判定から入れます。
実装としてはひどく単純で、最初のviewDidloadの最後に記載しているif文の箇所で処理をしています。myDog変数は本来指定した犬の画像名を受け取る変数ですがもし選択画面で全犬モードの箇所をタップした場合にはmixと代入される様にして、mixだった場合全犬表示するallDog()、そうでなければ指定の犬一匹だけのoneDog()関数を呼び出すようにしてあります。
oneDogは先程作ったcreateScene関数に画像名と、画面中央あたりにくるような座標を指定してあげてsceneを作成してもらいそれをsceneViewに追加する実装です。
allDogは単純に全犬情報が画面にきれいに並ぶように指定して、sxeneViewにすべて追加してあげます。
これで、AR画面の実装は(とりあえず)完了です。
④:選択画面
選択画面に関しては、ストーリボードに犬のボタン画像と「大きくする」「丸くする」などのスイッチを配置し、該当ボタンをタップした段階で各々のフラグと犬画像名をsegueとprepareで渡してあげるだけの構造でAR絡みの実装は皆無なのでここでは割愛します。
⑤:実機検証
最後に早速実機で動かしてみます。発表前提なのでiPadで動かしています。
(noteにmp4動画貼れないのでスクショですが...)
これが選択画面になります。
下の3つで表示する犬ARを「おおきくするか」「回転させるか」「丸くするか」を選べます。数値選べるともっと自由度高くて面白いんですが、タップ操作だけの単純アプリとしているので今回はこれだけで(時間もないので)
そして、犬画像をタップするとAR画面が起動して、カメラ越しに犬画像が表示されます。
ではさっそく......
おぉ......
でてくる......
画面起動した時の位置で固定されるので、表示したあとは自分が動いてもその画像はその座標に固定されているので、本当に現実の位置にある様な感じに見えます。
全犬表示。今思うとなんてつまらない座標配置......もっと遊べばよかった。
まとめ
こんな感じでARのさっくりお試しアプリを作成しました。
本来はちゃんと対応した3Dモデルデータを宛ててあげると、ゲームのようなキャラモデリングが表示されてもっとテンションが上がると思うので機会があればちゃんとした3Dモデルを表示してみたいですね。
今回は1〜3歳くらいのこどもがちょっと遊べる感じのものとして作ってみたので、うちのmarchilyの犬たちにご参加いただきました。
次もちょっと新し目な技術に触った感想記事などが書ければと思います。
ひぇ……(丸くするにした時の表示)