水平面を検出するためのコンフィグレーションを設定
現実世界の水平面を検出するためにの設定をします。
sceneViewの型はARSCNViewクラスです。デフォルトでは、平面検出は行われません。必要な機能はプログラマーが各自設定する必要があります。
let configuration=ARWorldTrackingConfiguration()
//水平面の時は.horizontalで垂直面の時は.verticalになります。
configuration.planeDetection = .horizontal
sceneView.session.run(configuration)
検出した平面をどのように扱うのか?
ARSessionDelegateを処理したいクラスに批准させて、そのクラスをARSceneView.session.delegateに代入して、デリゲートメソッドを利用します。
sceneView.session.delegate=self
ここで、selfを代入させたので、このクラス(Viewcontroller)上でデリゲートメソッドを利用できます。
//アンカーがセッションに追加されたときに呼ばれる
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
}
//アンカーが更新されたときに呼ばれる
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
}
アンカーとは検出物体の位置や向きを表すクラスです。
ARanchorクラスはプロパティとしてindentifierとtransformを持っています。
indentifierはそのアンカーの名前です。人間でも名前で他人と区別するように、アンカーにも名前をつけて他のアンカーと区別をします。つまり、アンカーを一意に定めるものです。transformは、位置や大きさを表すプロパティで4×4行列です。
ここで、anchorのままでは使いにくいので,サブクラスであるARPlaneAnchorにキャストします。
//アンカーがセッションに追加されたときに呼ばれる
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
guard let planeAnchors=anchor as? [ARPlaneAnchor] else {return}
}
ARPlaneanchorクラスは,centerプロパティとextentプロパティを持ちます。
centerプロパティは平面アンカーの中心の位置で、extent(simd_float3型)は,平面の大きさを表しますが、平面なのでyの値は0になります。
検出した平面に色を塗りたいときこのメソッドは使いません。
AR機能はセッション単位で管理しています。
上のメソッドでは、平面に限らず、seceViewにアンカーが追加されたり、更新されたりするときに呼ばれだけで検出した平面のアンカーの位置や大きさを所得して、色を変えたりするのは別のメソッドで行います。
平面検出したときに呼ばれるメソッド
ARSCNViewDelegateプロトコルのデリゲートメソッドを使います。
//平面アンカーのノードがシーンに追加されたときに呼ばれる
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// アンカーを平面アンカーにキャストする
guard let planeAnchor=anchor as? ARPlaneAnchor else {return}
// アンカーから平面形状(geometry)の平面を作成
let geometry=SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
// 平面に黄色を付ける
geometry.materials.first?.diffuse.contents=UIColor.yellow.withAlphaComponent(0.8)
// 平面からノードを作成する。
let planeNode=SCNNode(geometry: geometry)
// なんか知らんけど、平面がx-y平面上に作られるので,平面をx軸(1,0,0)を軸に90°回す
planeNode.transform=SCNMatrix4MakeRotation(-Float.pi/2, 1, 0, 0)
// メインスレッドでnodeに追加する
DispatchQueue.main.async(execute:{
node.addChildNode(planeNode)
})
}
nodeは平面アンカーに対応したnodeです。