はじめに
HealthCare Innovation Group(HIG)1の橋本です。
先週末注文していたAirPods Pro第2世代が今日手元に届きました!
約4年間使っていたAirPods Pro第1世代の調子が悪くなってしまったため、買い換えました。
せっかく新しいAirPods Proが届いたので、なにかできることないかな〜と思いながら、AirPods Proの機能一覧を見ていました。
私はその中の1つ、空間オーディオ機能でヘッドトラッキングしていることに目をつけ、頭の角度の取得をしてみました。
環境
- OS: macOS Sonoma 14.5
- Xcode: 15.4 (15F31d)
- Swift: 5.10
- AirPods Pro(第2世代)
※ 空間オーディオ機能搭載端末
AirPods(第3世代)、AirPods Pro(全世代)、AirPods Max
(参考URL: AirPodsユーザガイド 空間オーディオとヘッドトラッキングを操作する)
今回作ったもの
AirPodsProで取得した頭の角度でリアルタイムにキャラクター動かせるミニアプリ。
キャラクターが動いている部分は、次のように、私がAirPods Proをつけた状態で頭を傾けたり、回転したりしています。かなりよい感度で表示できているなと感じます。
このミニアプリで使用したフレームワークや各種APIを紹介していきたいと思います。
CoreMotionフレームワークとは
CoreMotionとは、各種Appleが提供するハードウェア(iOS, iPadOS, watchOS, visionOS device)のモーションデータを取得できるフレームワークです。
CoreMotionフレームワークのひとつのクラスとして、CMHeadphoneMotionManager
があります。
CMHeadphoneMotionManager
このクラスを使うことで名前の通り、ヘッドフォンのモーション情報をアプリに提供することが可能になります。
使用するためには、Info.plist
でNSMotionUsageDescription
キーを追加し、モーションデータを使用する具体的な理由を記載する必要があります。
注意NSMotionUsageDescription
キーが存在しない場合、対応するデバイスのモーションデータを更新しようとするとアプリがクラッシュしてしまいます。
今回使用するCMHeadphoneMotionManager
が持つ主なメソッドについて、説明します。
isDeviceMotionAvailable
接続している端末でCoreMotionが使えるか否かをBool値で返すメソッド。
startDeviceMotionUpdates(to:withHandler)
モーションデータの更新を開始し、データの更新を指定のキューに送信するメソッド。
stopDeviceMotionUpdate()
モーションデータの更新を停止するメソッド。
実際にアプリを作ってみる
事前準備
CoreMotionフレームワークが使えるように、Info.plistにNSMotionUsageDescription
キーを追加、説明を記載します。
Info.plist
project.pbxproj
INFOPLIST_KEY_NSMotionUsageDescription = "To sync the movements of the character’s head on the screen with your head movements for an enhanced interactive experience."; |
これで、次のようにモーションデータを取得する前に、許可を求められるポップアップが表示されます。
HeadTrackingを管理するクラスを用意する
HeadTrackingを監視するために、HeadTrackingManagerクラスを定義します。このクラスを使うことで、モーションデータを取得し、リアルタイムでSwiftUIビューに反映させることができます。
次に、コード全体を共有します。
import CoreMotion |
全体概要
このクラスはObservableObject
プロトコルに準拠させ、SwiftUIビューと連携して動作させるようにしています。
各プロパティの説明
motionManager: CMHeadphoneMotionManager
MHeadphoneMotionManager のインスタンスで、ヘッドモーションデータの取得を管理する。@Published var pitch: Double
頭のピッチ(上下の回転角度)を表す。@Published var roll: Double
頭のロール(左右の傾き角度)を表す。@Published var yaw: Double
頭のヨー(水平面内の回転角度)を表す。
これらのプロパティは、@Published
が付与されているので、変更があったときにはビューを自動的に更新する。
次に、各メソッドを説明します。
startTracking()
モーションデータの取得を開始するメソッド。
func startTracking() { |
モーションデータを提供できるかどうかをisDeviceMotionAvailable
で確認します。その後、モーションデータの更新を開始し、データが取得されるたびにTask
クロージャ内で、updateMotionData(_:)
メソッドを呼び出します。
stopTracking()
モーションデータの取得を停止するメソッド。stopDeviceMotionUpdates()
メソッドを呼び、モーションデータの取得を停止します。
func stopTracking() { |
updateMotionData(_ motion: CMDeviceMotion) async
モーションデータの取得、更新を行うメソッド。
private func updateMotionData(_ motion: CMDeviceMotion) async { |
UI更新を安全に行うために、MainActor.run
内でUIの更新を行うことでUIスレッドの競合が発生しないようにしています。
取得した姿勢データattitude
を使用して、pitch
、roll
、yaw
の更新を行います。
Viewに反映させる
最後に、取得したデータを元にキャラクターを3次元空間でぐりぐり動かせるようにします。
Image()
をロール、ピッチ、ヨーで3次元回転させるように、.rotation3DEffect()
を使用します。
ヨーに関しては、内側のカメラで自分を撮影するときと同様に、キャラクターが動くようにY軸周りの回転角を負の値に設定しています。
Image("monster") |
ボタンを押下するごとにHeadTrackingの計測状態と停止状態が入れ替わるようにします。
Button { |
このボタンを押下することで、HeadTrackingが開始します。
全体のコードは次に記載しているので、手元で試してみてください。
コード全体
HeadTrackingApp.swift
import SwiftUI |
HeadTrackingManager.swift
import CoreMotion |
ContentView.swift
import SwiftUI |
さいごに
CMHeadphoneMotionManager
を使うことでとても簡単にHeadTrackingを行うことができました!
今後は、今回取得した頭の角度データを時系列データとして扱い、HealthKit
フレームワークのHKHealthStore
に保存したりして健康促進に活用できないか等、色々検討したいと思います。
参考
- AirPods Pro(第2世代)
https://www.apple.com/jp/airpods-pro/ - 空間オーディオとヘッドトラッキングを操作する
https://support.apple.com/ja-jp/guide/airpods/dev00eb7e0a3/web - AirPods Proの加速度センサーの値を取得する【iOS14】
https://qiita.com/tukutuku_tukuyo/items/ea949ee2dbb499d6e7ca - Core Motion | Apple Developer Documentation
https://developer.apple.com/documentation/coremotion - CMHeadphoneMotionManager | Apple Developer Documentation
https://developer.apple.com/documentation/coremotion/cmheadphonemotionmanager - isDeviceMotionAvailable | Apple Developer Documentation
https://developer.apple.com/documentation/coremotion/cmmotionmanager/1616094-isdevicemotionavailable - stopDeviceMotionUpdates() | Apple Developer Documentation
https://developer.apple.com/documentation/coremotion/cmmotionmanager/1616115-stopdevicemotionupdates - startDeviceMotionUpdates(to:withHandler) | Apple Developer Documentation
https://developer.apple.com/documentation/coremotion/cmmotionmanager/1616048-startdevicemotionupdates - かわいいフリー素材集『いらすとや』
https://www.irasutoya.com/
- 1.医療・ヘルスケア分野での案件や新規ビジネス創出を担う、2020年に誕生した事業部です。設立エピソードは次の記事をご覧ください。”新規事業の立ち上げ フューチャーの知られざる医療・ヘルスケアへの挑戦” ↩