こんにちは.株式会社ZOZOでiOSエンジニアとして内定者アルバイトをしている@thenagainです.
本記事はZOZO Advent Calendar 2021のカレンダー5の17日目の記事です.
#はじめに
ナンバリングが増すごとにワクワクする機能もどんどん増えていくARKitですが,今年にリリースされたARKit 5の主なアップデートは以下の2点です.4と比べるとちょっと寂しい😢
-
フェイストラッキングの対応拡大
- A12 Bionicチップ以降を搭載したすべてのデバイスのフロントカメラで利用可能に
-
ロケーションアンカー
- 街中や人気のスポットなど,特定の場所にAR体験を配置
今回はその対応拡大したというフェイストラッキングを使ってゲームでよくみるホイール入力を実装してみました.
ゲームでよくみるホイール入力の例はこちら ↓
ボタンを押している間,ホイール状のUIが表示されて,特定の方向へマウスやスティックを向けることで選択し,ボタンを離すことで入力が確定するというものです.(個人的にこの操作感が大好き)
今回はこのような入力を,画面へのタップと顔の向きで再現してみました.具体的には,画面をタップしている間,ホイール状のUIが表示されて,特定の方向へ顔を向けることで選択し,タップを離すことで入力が確定する,という流れです.
実際の動作の様子はこんな感じ ↓
8つの絵文字がホイールに表示されていて,それを顔の向きで選択している様子となります.分かりやすいように少し大きめに頭を振っていますが,ブンブンと頭を振らなくても入力できます😉
#実装
キモとなる顔の向きによる選択ですが,下図のようにして角度を算出し,その角度をホイール状のUIに反映させるという実装にしました.
コードにするとこんな感じ
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
currentTransform = anchors.compactMap({ $0 as? ARFaceAnchor }).first?.transform
if let initial = initialTransform, let current = currentTransform {
let diff = simd_mul(simd_inverse(initial), current)
let zvec = simd_float4(x: 0, y: 0, z: 1, w: 0)
let raycast = simd_mul(diff, zvec)
let raycast_xy = simd_float2(x: raycast.x, y: raycast.y)
guard simd_length(raycast_xy) > deadZone else {
// デットゾーンよりも小さい場合は未選択とする
// (省略)
return
}
var theta: Float = acos(simd_normalize(raycast_xy).x)
theta = simd_normalize(raycast_xy).y < 0 ? 2 * .pi - theta : theta
// 得られた角度thetaをUIに反映させる処理
// (省略)
}
initialTransform
がタップ開始時のARFaceAnchor
のtransform行列で,currentTransform
が現在のtransform行列です.これをもとに行列計算によって,基準平面上でのレイキャスト座標raycast_xy
を算出し,この長さがデッドゾーンよりも大きい場合に角度を求めてUIに反映させるという処理になります.
#まとめ
今回はARKitのフェイストラッキングを使って,ゲームでよくみるホイール入力を実装してみました.
とりあえずホイールは8分割として実装してみましたが,動かしてみるともっと分解能が高いと感じました.
ARKitのトラッキング精度,素晴らしいですね…!