前回最後に紹介したARサンプル、無事実機動作したっすか〜?
今回は、このARサンプルを作るために、Single View Appテンプレで用意したプロジェクトにAR機能を組み込んでていく過程を解説しマッスルメモリー。
前回のARサンプルなどすでに読み終えたわぁな剛の者(リブートキャンプを読んで、UIView、UIViewControllerの関係や、クラス継承、知らないクラスやメソッドを調べる方法を学習してれば可能と思われ)は復習に使ってください。
前回のARサンプル:
http://tetera.jp/xcc/book-sample/ar.zip
実機で動かす時はTeamを自分のApple IDにしてね
まずは前回用意したSingle View Appテンプレのプロジェクトを開きましょう。
あのプロジェクトのアプリをiPhone実機で動作させても、単に白い画面が出るだけってのは前回経験済みっすね。で、この白い画面にARアプリらしくカメラのライブ画像を表示させて、その中に3Dの仮想体を配置したいわけですよ。
そのために必要なのが、UIViewを派生してAR画面を表示するために用意されたARSCNViewオブジェクトっす。
ARSCNView
こいつはカメラのライブ画像と3Dの仮想体を合成した画像を表示するもんです。こいつを作成してself.viewに子画面として登録するのが第1ステップ。
そのために触るのは、おなじみViewController.swiftのviewDidloadメソッドすね。viewDidloadが何かわからん人は「こんにちは世界」を読みましょう。
import UIKit import ARKit // ARSCNViewを使うための前準備 class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // ARシーンを表示するビューの作成と // self.view子画面としての登録 let sceneView = ARSCNView(frame: CGRect(x: 50, y: 50, width: 200, height: 300)) self.view.addSubview(sceneView) }
ViewControllerクラス定義の前にやってるimport ARKitってのはARSCNViewを使うための前準備。ARSCNViewはARKitっていうフレームワークでクラス定義等が用意されてるので、その情報を輸入する(import)って意味っす。
ARSCNViewの作成時に指定してる矩形は適当。皆さんも適当に好きな大きさに設定してください。
ARSCNView(frame: CGRect(x: 50, y: 50, width: 200, height: 300))
画面全体にARSCNViewを表示しなくても問題はない。この点、色々応用が効きそう。
おっしゃ〜、これで準備OK、とりあえずライブ画像は表示されるはず!
さっそくRunやあ!
ってやってもいいんですが、これだと画面は真っ白のままっす。
ていうのも、ライブ映像用のカメラや、カメラから取り出したライブ映像と3Dの仮想体を合成したりするするために使うジャイロスコープなんかは、色々なアプリで使い回す共有物なんで、自分だけで独占できないんですな。
そのためiOS側と「カメラ使いまーす」、「カメラ使い終わりましたー」的なやりとりが必要で、使ってる期間をAppleではセッションと呼んでます。
カメラを使うセッションとか、スピーカーやマイクを使うセッションとかがあって、セッション管理用の専用オブジェクトが用意されてて、ARの場合はARSessionてオブジェエクトを使います。
ARSession
で、ARSessionオブジェクトはARSCNView作成時に一緒に作成されてて、ARSCNViewのsessionプロパティに設定済みなんですよ。
なので、あとは「ARセッション開始してー」、「ARセッション終了してー」ってメッセージをARSessionオブジェクトに送ればライブ画像の表示や3Dの仮想体合成が始まります。
あとは、そのメッセージをいつ送るのかってことになるんだけど、一般的にはARSCNViewが表示される時にセッション開始、非表示になる時に停止ってことになるわけですよ。
そのタイミングを知らせてくれるのがUIViewControllerにあらかじめ用意されてるviewWillAppear、viewWillDisappearってメソッドっす。
self.viewが作られた直後に呼ばれるのがviewDidLoadメソッドだったわけっすが、それと同じようにself.viewが表示される直前に呼ばれるのがviewWillAppear、非表示になる直前に呼ばれるのがviewWillDisappearっす。
ARSCNViewはself.viewの子画面なので、表示・非表示はself.viewと連動するからね。
というわけで、各メソッドを次のようにオーバーライドしてプログラム的には準備完了。
class ViewController: UIViewController { // ARシーンを表示する var sceneView:ARSCNView! ← viewWillAppearやviewWillDisappearで 参照するのでプロパティにする override func viewDidLoad() { super.viewDidLoad() ↓ viewDidLoad内で宣言してるので、viewDidLoad内だけで 有効(ローカル定数)になっている点に注意 let sceneView = ARSCNView(frame: CGRect(x: ・・・ ・・・ ↓ sceneViewプロパティ側にsceneViewローカル定数の参照値をコピー self.sceneView = sceneView } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // ARセッションの初期設定 let configuration = ARWorldTrackingConfiguration() // ARセッション開始 sceneView.session.run(configuration) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // セッション停止 sceneView.session.pause() } }
ちなみにviewWillAppear、viewWillDisappearメソッドは、どちらも追加書き込みになるんだけど、バカ正直にoverride func viewWillAppear・・・ なんて書こうとせずXcodeの支援機能を使いましょう。
例えばメソッドを追加したい場所に「viewWill」と書くと、「viewWill」に関連するメソッド等の候補一覧が出てくれるので、そこから選ぶといいです。オーバーライド指定とかも勝手にやってくれる。
注意)「view」あたりからメニューは表示されて「viewW」、「viewWi」と書き込み量を増やすことで候補を絞り込めるようになってる。
オーバーライドしたviewWillAppearや、viewWillDisappearではsuper側を呼び出します。オーバーライドやsuperの意味がわからん人は「こんにちはデベロッパの世界」を読みましょう。
注意)実のところ派生元のUIViewControllerクラスではviewWillAppearや、viewWillDisappearメソッドは何もやってないので、今のところ呼び出さなくても支障はないんすけどね。
それと、作成したARSCNViewオブジェクトの参照は、viewDidLoadメソッドだけでなくviewWillAppearメソッドや、viewWillDisappearメソッドでも使うのでViewControllerのプロパティとして用意します。
class ViewController: UIViewController { // ARシーンを表示する var sceneView:ARSCNView! ← viewWillAppearやviewWillDisappearで 参照するのでプロパティにする
プロパティがわからん人は「そしてiPhoneアプリへ」を読みましょう。
でもってviewDidLoadメソッドの最後にメソッド内で作ったARSCViewの参照値をsceneViewプロパティにコピーします。
viewDidLoadメソッド内で、letを使って用意したsceneView定数は、viewDidLoadメソッド内だけで有効(ローカル)な定数なんで、メソッドから戻る時に破棄されちゃうんですよ。なのでプロパティにコピー。
override func viewDidLoad() { let sceneView = ARSCNView(frame: CGRect(x: ・・・ ・・・ ↓ sceneViewプロパティ側にsceneViewローカル定数の参照値をコピー self.sceneView = sceneView }
今回だとローカル定数の名前もsceneViewなんで、ローカル定数側とプロパティ側を区別するためにプロパティ側にはself.を付けてます。ローカル変数や定数にself.は付けられないんで、これで区別がつく。
プロパティ名の前につけるself.の役割がわからん人は「こんにちはデベロッパの世界」を読みましょう。
注意)わざわざself.を付けて区別しなくても、最初から名前を分ければいいんだけど、私は関係性がわかるので同じにするようにしてます。他にプロパティ側には必ず先頭に「_」を付けるとか人によって色々ルールがある。グループで開発するときは、ここらへんを皆んなで話し合って統一してると他人のソースを読む時わかりやすい。
そいうわけで、viewWillDisappearメソッドで使ってるsceneViewはプロパティのことです。メソッド内に同名のローカル定数/変数がないのでself.を省略してる。
override func viewWillAppear(_ animated: Bool) { ・・・ ↓self.を省略してるself.sceneView.session.run(configuration) }
注意)プロパティ名と同じ名前のローカル変数や定数を使ってないメソッド内ではself.は省略可能。なんだけど、これもプロパティには必ずself.付けるとか、人によって様々。まあ、self.付けた方が明示的ではある。
ちなみにXcodeはデフォルトでプロパティにだけ色付けしたりしてます。
で、このviewWillAppearメソッドでやってる
sceneView.session.run(configration)
てのが開始メッセージの送信っす。sceneView.sessionプロパティに設定されてるARSessionにrun(configration)メッセージを送ってるわけですよ。
runメソッドはARConfigurationオブジェクトを引数に取るので、メッセージでは事前にデフォルト値が設定されたARWorldTrackingConfigurationを作成して指定している。
当然だけどARWorldTrackingConfigurationはARConfigurationからの派生ね。
// ARセッションの初期設定 let configuration = ARWorldTrackingConfiguration() // ARセッション開始 sceneView.session.run(configuration)
でもってviewWillDisappearメソッドでやってるのが停止メッセージの送信すね。
// セッション停止
sceneView.session.pause()
これで本当にプログラム的には準備完了なんですが、このまま実機でRunさせると死にます。
↓試した人はこうなると思うんで、ストップボタンで終了ね。
これはiPhoneのセキュリティ面での措置で、カメラやアルバム、GPSといったプライバシーに関連する装置や資産を利用するアプリは、利用することをあらかじめ表明しておく必要があるからです。
今回の場合はカメラね。で、カメラ利用表明は、ナビゲータエリアでプロジェクトアイコン選んでTARGETをarにしてinfoタブを選んで表示される一覧に以下の項目を追加することでおこなう。
Privacy - Camera Usage Description : ARのため
やり方はこんな感じ。
Value列に書き込むメッセージは"ARのため"でなくてもいいです。好きなこと書いていいけど、書き込んだメッセージはカメラを初めて使う時に現れるアラートのメッセージとなるんで、カメラを使う理由がユーザーに伝わる内容がベスト。
これでようやく実機で動かせるようになる。
カメラからのライブ画像は出たかな〜。
ま〜、でもこれだけだと、ただのライブカメラだよね。ここに3D仮想体が合成されてこそAR。その準備はできてるぜ。ということで、次回は3D仮想体である「こんにちはAR」文字列の配置について。
じゃまた。