ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Reality Composer Proにおけるインタラクティブな3Dコンテンツの作成
3Dコンテンツに生命を吹き込む、Reality Composer ProのTimelineビューについて解説します。インバースキネマティクス、ブレンドシェイプ、スケルトンポーズを使用して、キャラクターやオブジェクトが互いにやり取りしたり、それらを取り巻く世界と関わったりするアニメーションストーリーを作成する方法をご紹介します。組み込みのカスタムアクションの使用方法、アクションシーケンスの作成方法、トリガーの適用方法、自然な動きの実装方法についても説明します。
関連する章
- 0:00 - Introduction
- 2:54 - Introducing timelines
- 18:22 - Inverse kinematics
- 22:55 - Animation actions
- 27:23 - Blend Shapes animation
- 30:35 - Skeletal poses
リソース
関連ビデオ
WWDC24
- カスタム環境でのよりイマーシブなメディア視聴体験の実現
- 空間コンピューティング向けに3Dアセットを最適化
- iOS、macOS、visionOS向けRealityKit APIの紹介
- RealityKitデバッガの詳細
WWDC23
-
ダウンロード
こんにちは Marinです RealityKit Toolsのエンジニアです 本日ご紹介するのは Reality Composer Proの まったく新しい機能「Timeline」です 新しいインタラクティブな方法で 3Dコンテンツに命を吹き込むことができます 昨年 Appleが発表した Reality Composer Proでは アプリで使用する3Dコンテンツの プレビューと準備を 簡単に行うことができます このツールのビジュアルエディタでは シーンを作成したり アセットを整理したり インタラクティブ機能や物理特性を追加したり または ShaderGraphエディタを使用して マテリアルの外観を微調整したりと 様々なことが行えます Reality Composer Proを初めて使う場合や 使用方法を再確認したい場合は 昨年のWWDCのこれらのセッションを ぜひご覧ください Reality Composer Proでは RealityKitコンテンツを視覚的に デザイン 編集 プレビューできます シーンの構成 コンポーネントの設定 複雑なマテリアルの作成 音声の追加など 様々な操作をすべて1か所で行えます 今年は 新しい機能を追加しました タイムラインエディタを使用して アニメーションを作成したり ライトを追加したり 環境オーサリングをサポートする機能です このセッションでは タイムラインエディタに焦点を当て Timelineを使用して インタラクティブなアプリを開発する方法と RealityKitの新しい アニメーションAPIについて説明します さっそく見てみましょう
他のセッションでも このBOT-anistアプリを ご覧になっているかもしれません このアプリのロボットを利用して 新たに作り直し 独自のインタラクティブなバーチャル体験を 作成してみましょう このアプリを見ると 3種類の植物がしおれ始めています しおれている植物をタップすると ロボットがその植物まで移動して 水やりを始めます 植物はゆっくりと元気を取り戻し ロボットはプラットフォームの 中央に戻ります また別のロボットは 周囲を飛び回る蝶に 手を伸ばして触ろうとしています このセッションのリンク先から サンプルプロジェクトをダウンロードできます このアプリを構築するには まず Reality Composer Proの Timelineを使用して 植物の位置までロボットを移動させます
次に RealityKitの フルボディインバースキネマティクス システムを利用して ロボットを動かし 植物に 水やりをさせる方法を説明します
その後 ロボットを方向転換させ 開始位置まで戻すための アニメーションアクションを コードで記述する方法を 見ていきます
さらに ブレンドシェイプ アニメーションを使用して 植物をアニメーション化する方法を紹介します
最後に 蝶を追っている2体目のロボットに スケルトンポーズアニメーションを追加します
では Timelineの概要から始めましょう TimelineはReality Composer Proの まったく新しい機能です 特定の順序で または特定の時間に 実行させるアクションの シーケンスを作成できます Reality Composer Proを使用すると これらのアクションの編集や設定を 簡単に行えます 左側のパネルには すべてのタイムラインがリスト表示されます
中央はメインのタイムラインエディタです 右側のパネルにはビルトインアクションが すべてリスト表示されます タイムラインを作成すると トリガーに基づいて再生し タイムラインを開始することができます
最初のアニメーションを見てみましょう このアプリでは 植物をタップすると ロボットがその植物の方向に動き出します このタイムラインシーケンスの 作成方法をご紹介しましょう Reality Composer Proを開くと 下部のパネルに という新しいタブがあります 中央にはボタンがあります タイムラインを作成するため このボタンを選択しましょう 左側には 利用可能な すべてのタイムラインがリスト表示されます 中央がメインのタイムラインエディタです ここでアクションを設定して タイムライン上に並べることができます 右側には あらかじめ用意しておいた すべてのビルトインアクションが リスト表示されています これらをタイムライン上に 簡単にドラッグ&ドロップできます タイムライン名を 「MoveToPoppy」に変更しましょう タイムラインを選択して ダブルタップし 「MoveToPoppy」と入力します
まず ロボットがタップされた 植物の方を向くようにします そのためには 既存のアクションリストにある Spinアクションを使用します アクションを タイムラインまでドラッグします
ロボットを回転させたいので インスペクタパネルのフィールドで を選択します 次に 階層ツリーまたはビューポートで ロボットエンティティを探し それを選択します 目的のエンティティを選択したら をタップします
次に ロボットをどの程度 回転させるかを設定しましょう インスペクタパネルの を使用します これを「0.12」に設定しましょう
1回転ではなく 少しだけ回転させます 実際に見てみましょう タイムラインエディタの上に 再生ボタンがあります このボタンをタップすると 設定したアクションをプレビューできます
いいですね ロボットが少し回転して 植物の方を向きました 回転したあと ロボットを移動させてみましょう そのためには Transform Toアクションを使用します ビルトインアクションリストから アクションをドラッグして エディタ上にドロップします Spinアクションの1秒後に実行されるように シーケンスを設定します ロボットが植物に向かって 移動するようにしたいので このアクションのターゲットを ロボットに設定しましょう をタップして 階層ツリーでロボットを選択し をタップします ロボットの移動方向を視覚的に示す グラフィックスが表示されます 現在はプラットフォームの中央にいるので 変更する必要があります マニピュレータを使用して ポピーの前までロボットを動かします
これで大丈夫です 次に Transformアクションの 継続時間を変更しましょう デフォルトの継続時間は1秒ですが もっと大きい値に変更します ロボットがより長い時間をかけて 植物まで移動するようになります そのためには エディタでアクションの 右端をドラッグします
または インスペクタパネルで を設定します
アクションをドラッグして 別のトラックへ移動できます これにより 複数のアクションを 同時に実行させることができます Spinアクションを別のトラックまで ドラッグしましょう これは後で使用します 再生ボタンをタップして どのように表示されるか プレビューしましょう
いいですね 希望どおりになりました ロボットが植物まで移動する間に適用される アニメーションと音声を追加しましょう そのためには 別のタイムラインを作成して 歩行のアニメーションと音声が 同期するようにします タイムラインリストの下部にある ボタンをタップします このタイムラインの名前を 「RobotMove」に変更しましょう 新しいタイムラインを選択してダブルタップし 「RobotMove」と入力します
アニメーションと音声を再生するため 2つの新しいコンポーネントを使用します Animation Libraryコンポーネントと Audio Libraryコンポーネントです これら2つのコンポーネントについて 説明しましょう Animation Libraryコンポーネントは アニメーションを保存し それを再生するエンティティと 関連付けるために使用されます Animation Libraryコンポーネントを 対象エンティティに追加し そのAnimation Libraryコンポーネントに アニメーションリソースを追加します Reality Composer Proでは Animation Libraryコンポーネントに アニメーションを簡単に追加できます ボタンをタップして プロジェクトからアニメーションを選択します これで そのエンティティの Animation Libraryコンポーネントに アニメーションリソースが追加されます これは このあとコードを使用して または Reality Composer Proでタイムラインを使用して アニメーションを再生するときに 使用できます
Audio Libraryコンポーネントも Animation Libraryコンポーネントと よく似ています Audio Libraryコンポーネントを使用して 音声リソースを保存し それらを再生するエンティティに関連付けます Audio Libraryコンポーネントを エンティティに追加し そのAudio Libraryコンポーネントに 音声リソースを追加します Audio Libraryコンポーネントに 音声リソースを追加するには ボタンをタップして プロジェクトから音声ファイルを選択します これで そのエンティティの Audio Libraryコンポーネントに 音声リソースが追加されます
そのあとコードで またはタイムラインを使用して 音声を再生できます Reality Composer Proに戻って 音声とアニメーションの タイムラインへの追加を完了させましょう RobotMoveタイムラインで 新しい Animation Libraryコンポーネントを使用して Animationアクションを追加します これから使用するプロジェクト画面に すでにUSDアニメーションが追加されています 階層ツリーでロボットを選択します インスペクタパネルを見ると 既にAnimation Libraryコンポーネントが 追加されていることがわかります これは このエンティティに 既にUSDアニメーションが 関連付けられているためです Reality Composer Proによって このコンポーネントが自動的に追加され デフォルトのアニメーションが アニメーションリストに表示されます インスペクタパネルの下部にある ボタンを選択して Animation Libraryコンポーネントを 手動で追加することもできます を 選択すると 再生ボタンを使用して このアニメーションをプレビューできます または はさみアイコンを使用して 複数のクリップに分割できます このアニメーションは少し長いので 2つのクリップに分割しましょう 短縮した前半と後半のアニメーションを 続けて再生します そのためには 再生ヘッドをドラッグして スライスしたい最初の位置まで動かします 次に はさみアイコンをクリックします 2つのクリップが作成されます もう1か所スライスして 最後の部分だけを使用します もう一度再生ヘッドをドラッグして 2番目のスライス位置まで移動します はさみアイコンをクリックします
これで2つのクリップを 続けて再生できるようになりました 2つのクリップの名前を 「startWalk」と「endWalk」に変更します
次に RobotMoveタイムラインを選択します これらのアニメーションが 続けて再生されるように並べましょう Aanimationアクションを タイムラインまでドラッグします
このアクションのターゲットを ロボットにするため を選択した後 階層ツリーでロボットを選択します 次に を クリップに設定します
2つ目のクリップ用として 別のAnimation アクションをタイムラインまでドラッグします
このアニメーションは 最初のアニメーションの直後に実行します このアクションも ターゲットをロボットに設定します
をendWalkクリップに設定します
アニメーションの実行中 音声クリップも 同時に再生しようと思います そのためには Audio Libraryコンポーネントと 既にプロジェクトに追加してある 音声ファイルを使用します 階層ツリーでロボットを選択しましょう インスペクタパネルの ボタンを選択して 新しいコンポーネントを追加します リストから Audio Libraryコンポーネントを選択します
ボタンをタップして
必要な音声ファイルを見つけます ここでは2つの音声ファイルを追加します ロボットの方向転換時の音声と 移動する時の音声です
RobotMoveタイムラインに戻って 音声アクションを実行する タイミングを設定しましょう Play Audioアクションを タイムラインにドラッグします ロボットが音声を発するようにしたいので としてロボットを選択します 先ほど追加した歩行用の オーディオクリップを再生するため オーディオリソースとして 歩行用音声を設定します さらに このオーディオアクションを ドラッグして 歩行アニメーションと同時に 再生されるようにします
では 再生してみましょう
アニメーションと音声が 一緒に再生されました これで RobotMoveタイムラインを MoveToPoppyタイムラインで 使用する準備が整いました MoveToPoppyタイムラインを選択しましょう 次に RobotMoveタイムラインを右クリックして オプションを選択します RobotMoveタイムラインが MoveToPoppyタイムラインに挿入されます これで タイムラインが別のタイムライン内で 再生されるようになりました ネスト化されたこのタイムラインは ドラッグして どこにでも移動できます 回転した直後に このアニメーションが 再生されるようにしましょう
さらに Transformアクションと組み合わせて このアニメーションと変形が 同時に発生するようにします Transformアクションの 継続時間を変更しましょう アニメーションの開始から 少し遅れて移動し始め アニメーション終了の少し前に 移動を終えるようにします 次に ロボットが回転するときに再生する 音声クリップを追加しましょう 既定のアクションのリストから Play Audioアクションをドラッグし Spinアクションと同時に 実行されるように配置します ロボットをエミッタとして設定し 音声リソースとして 回転用の音声を設定します
プレビューして どのように再生されるか確認しましょう
素晴らしい!ロボットがポピーまで移動し アニメーションと音声が再生されました タイムラインを作成しましたが この再生をどのように 開始するのでしょうか? 2つの方法があります 1つは コードでRealityKit APIを使用し タイムラインのアニメーション リソースインスタンスで entity.playAnimationを呼び出す方法です またはReality Composer ProのUIを使用して 新しいBehaviorsコンポーネントを追加します この場合 コードを記述しなくても タイムラインを起動して 再生を開始できます Reality Composer Proを使用して タイムラインの再生を開始するには タイムラインをトリガーさせるエンティティに Behaviorsコンポーネントを 追加する必要があります タイムラインの再生を 開始するために使用できるトリガーは 数種類あります タップ時 衝突時 シーンにエンティティが追加された時 コードで通知が渡された時です Reality Composer Proに戻り 植物へのタップジェスチャでタイムラインの 実行が開始されるようにしましょう タップジェスチャを設定するには まず タップする植物を選択します ここでは 階層ツリーで ポピーを選択します 新しいコンポーネントを追加するため インスペクタパネルで ボタンをタップし Behaviorsコンポーネントを選択します 次に ボタンをタップして タップトリガーを選択します ユーザーのタップに ポピーが反応するようにするためです ポピー草をタップしたとき MoveToPoppyタイムラインの再生が トリガーされるようにしましょう リストから MoveToPoppyタイムラインを選択します ここで タップジェスチャに反応するよう RealityViewに指示する必要があります Xcodeを見てみましょう RealityViewで タップジェスチャを追加します
タップジェスチャが targetedToAnyEntityになります ポピーをタップして指を上げたとき 渡された値からエンティティを取得し タップ動作をそのエンティティに適用します つまり 特定のエンティティをタップしたとき ここではポピーをタップしたとき Reality Composerで指定したタップ動作である MoveToPoppyタイムラインが適用されます これをビルドして実行すれば 動作を確認できます ポピーをタップして どうなるか見てみましょう
素晴らしい!ポピーをタップした時 ロボットが向きを変えて その方向に移動しました 最初のアニメーションを適切に作成し ユーザーの入力に基づいて 再生が開始されるようになりました すごいですね! 2つ目のアニメーションでは ロボットがポピーまで移動した後 その花に手を伸ばすようにしましょう そのためには Notificationアクションを追加して そのNotificationアクションを コード内でリッスンします 通知が発生したとき ロボットがポピーに手を伸ばすようにするための カスタムコードを記述します では Reality Composer Proに戻って Notificationアクションを追加しましょう
既定のアクションリストから Notificationアクションをドラッグして タイムラインの最後にドロップします
このNotificationアクションの ターゲットとしてポピーを設定します 次に この通知の識別名として 「ReachToPoppy」と入力します
この文字列は 通知が発生したときに渡されます Reality Composer Proで通知を設定したので この通知をリッスンするコードを ImmersiveViewに追加しましょう XcodeのImmersiveViewで 通知名「ReachToPoppy」を追加します 次に この通知のパブリッシャーを追加します
その後 onReceiveを使用して RealityViewでの通知の受信を 処理します 出力から通知を受け取った時 ユーザー情報辞書から ソースエンティティを取得できます さらに ロボットがポピーに 手を伸ばす動作を開始するための コードを記述します どのようなアニメーションにするかを ご覧ください ポピー草の前まで移動したロボットが その花に手を伸ばします
そのためには RealityKitのフルボディ インバースキネマティクスシステムを使用します インバースキネマティクスでは リグ付きスケルトン構造で 運動方程式を使用し 目的の位置に到達するための ジョイントの動きを決定します 目的の位置と 影響を受けるジョイントの 制約を指定すると そこに到達するまでのジョイントの位置が 自動的に計算されます 例えば 目的の位置まで手を動かす場合 その位置へ到達するように 肘の位置が自動的に調整されます これを使用すれば キャラクターの動きを 自然に見せることができます ここでは 新しい Inverse Kinematics APIについて 模式図を使用して説明し コードでの使用方法をご紹介します まず IKRigをインスタンス化します IKRigは インバースキネマティクスソルバが どのように動作するかを定義します IKRigにモデルスケルトンと イテレーション回数などの設定を追加します 同じエンティティで アニメーションを再生する場合 そのアニメーションに 重みを付加する必要があります IKの重みにより IKソルバは 何をオーバーライドするかを判断します 次に制約を追加します 制約とは ジョイントの 動きに対する制限です 制約を加えることで ひじを外側に曲げるなどの 不自然な動きを回避できます
次に 定義したIKRigをもとに IKResourceを作成します IKResourceはIKソルバが 処理で使用するランタイムデータです IKResourceからIKComponentを作成し それをエンティティに追加します IKComponentをインスタンス化する時点で このソルバを設定し 実行時にフレームごとに更新できます
ここで重要なのは RealityKitのIKソルバは ジョイント階層の一部だけでなく キャラクターの全身のスケルトンを 同時に解決する点です では インバースキネマティクスを使用して ロボットが植物に 手を伸ばすコードを記述しましょう
インバースキネマティクスソルバを 設定するには まず 空のリグを初期化して modelSkeletonで渡します 次に グローバルなリグ設定を更新します 試行錯誤の結果 maxIterationsを30に設定し globalFkWeightを0.02に設定すると 最もよいことがわかりました その後 リグのスケルトンで ジョイント名を参照します この例では ロボットの腰 胸 手を動かして 植物に手を伸ばす ロボットのアニメーションが 自然に見えるようにする必要があります
次に リグの制約を定義します ここでは 2つの親制約と 1つの点制約を設定します 親制約はジョイントの位置と 向きを制限します 点制約はジョイントの位置を制限します 次に このリグを含むリソースを作成し 指定したリソースを含む IKComponentを追加します フレームごとにIKターゲットを更新するため シーンでエンティティを見つけて それを配置します
まず 手を伸ばしたときの位置を取得します この位置は IKComponentが 追加されているエンティティを 基準にする必要があります reachPositionのx、y、zの位置を変更して フレームごとにわずかに異なるようにします
左手の制約もフレームごとに 更新する必要があります そのため エンティティから IKComponentを取得します 左手のターゲットの移動と 左手の位置を設定します 値0は IKによる制約への影響が ないことを意味します 値1は 制約に対して IKが完全に影響することを意味します
IKの開始時および終了時にロボットの腕を スムーズにアニメーションさせるため 位置がIKによってどの程度動かされるか そして時間の経過とともに 基準ポーズによってどの程度動かされるかの 重みを徐々に増減させます その後 更新した値を IKComponentにコミットします
コードを実行してみましょう
いいですね アニメーションの目標を達成しました ロボットが手を伸ばした後 パーティクルエフェクトを使用して 花に水をやります 花への水やりが終わったので ロボットを開始位置に戻しましょう そのためには Animationアクションを使用します Animationアクションを使用すると アクションが発生するタイミングを設定し そのシーケンスを特定の時間に わたって作成できます ゲームのカットシーンの アニメーションに似ていますが Animationアクションでは これをリアルタイムで行えます よくある使用例の1つに キャラクターが歩くときの フットフォールイベントがあります キャラクターの足が地面に着くたびに 音を鳴らします Animationアクションを使用し プラットフォームの中央まで ロボットを戻しましょう
アクションのAPIには 組み込みとカスタムがあります 組み込みアクションは複数の既定の アクションで構成され 簡単に設定できます SpinActionやPlayAnimationAction などがあります これらは Reality Composer Proの タイムライン機能の 基盤となるAPIです 組み込みアクション以外が必要な場合は カスタムアクションを使用できます 独自のアクションをコードで記述し タイムラインで同期させることができます 組み込みアクションAPIを使用するには まず 組み込みアクションを 使用するエンティティの アニメーション定義を作成します 次に その定義から アニメーションリソースを作成します 同時に 再生する アニメーションリソースをグループ化して それらを再生する順序に並べます 最後に エンティティで playAnimationを呼び出します これをコードで行う方法を確認しましょう
それぞれロジックを含む 複数のメソッドがあります これらは ロボットを回転 移動 位置調整するためのアニメーションを定義し アニメーションリソースを返します これらのアニメーションリソース インスタンスのロジックは 既存のRealityKit APIを使用して 定義されています アニメーションのシーケンスを作成します 先頭から RotateAnimation WalkAndMoveAnimation AlignAtHomeAction RobotTravelHomeCompleteAction の順になります 次に 既存のplayAnimation APIを使用して これらのアニメーションを この順序で再生します
カスタムアクションの場合 まず EntityActionに準拠した 独自のプロトコルを作成します 次に それをもとに makeActionAnimation APIを使用して AnimationResourceを作成します さらに そのAnimationResourceを 他とグループ化します または 特定の順序で 実行されるように並べます
最後に エンティティで Play Animationを呼び出します これで 実行時に カスタムアクションの 開始イベントと終了イベントを サブスクライブできます
これらのステップをコードで確認し 植物への水やりを終えたロボットが 開始位置に戻るようにしましょう RobotMoveToHomeCompleteという名前の カスタムアクションを作成します これを使用して move-to-home手順の 完了時に通知されるようにします 次に RobotMoveToHomeComplete EntityActionのインスタンスを作成します makeActionAnimation APIを使用して そこからAnimationResourceを生成します
注目すべき点は カスタムアクションを使用して 複数のアニメーションをシーケンス処理したり グループ化したりできることです ここではアニメーションが1つだけなので シーケンス処理は行いません
次に playAnimation APIを使用して AnimationResourceを再生します
EntityActionの開始イベントを サブスクライブします このアクションが開始された時点で サブスクリプションクロージャが呼び出されます 開始イベントが発生すると ロボットが目的地に 到着したことがわかります 再生コントローラを停止して アニメーションを終了します さらに RobotMoveToHomeComplete EntityActionの終了イベントも サブスクライブします クロージャが呼び出されたら ロボットを.arrivedHome状態に 遷移させます
では このアニメーションシーケンスを 実行してみましょう
いいですね 順調に進んでいます
次は植物のアニメーションに注目しましょう ここではブレンドシェイプ アニメーションを使用します ブレンドシェイプを使用すると 別のポーズへスムーズに遷移し リアルな動きを表現することができます 例えば キャラクターの顔や身体を アニメーション化するため 異なる一連のシェイプを ブレンドするときなどに使用されます ここでは しおれた植物が元気になる様子 またはその逆の様子を アニメーション化します
ブレンドシェイプアニメーションAPIは BlendShapeWeightsMapping BlendShapeWeightsComponent および アニメーションを手続き的に実行する方法と USDで実行する方法で構成されます BlendShapeWeightsMappingでは ブレンドターゲットに関連付ける 重みを設定できます エンティティでブレンドターゲットを設定し 各ターゲットにそれぞれ重みを 関連付けることができます 重みは0〜1で設定できます ここでは しおれた状態の植物に重み1 中間の状態に重み0.5 元気な状態に重み0を設定します しおれた植物が元気になる様子を アニメーション化するため 時間の経過と共に重み値を更新します ブレンドシェイプアニメーションAPIを 使用するには まず BlendShapeWeightsMapping を定義します 次に このマッピングから BlendShapeWeightsComponentを作成して それをエンティティに追加します BlendShapeWeightsComponentを エンティティに追加した後は このコンポーネントに重み値を照会して いつでも更新することができます これで FromToByまたはサンプリングされた ブレンドシェイプアニメーションを 手続き的に作成できるようになりました 既存の RealityKit playAnimation APIを使用して USDブレンドシェイプアニメーションを 再生することもできます コードでの作成手順を見ていきましょう まず エンティティ階層で モデルコンポーネントを含む モデルエンティティを見つけます そのメッシュリソースを取得し そこから BlendShapeWeightsMappingを作成します 次に このマッピングから BlendShapeWeightsComponentを作成します
実行時にブレンドの重みを更新するため エンティティから BlendShapeWeightsComponentを取得します
BlendShapeWeightsSetのコピーを取得します これで このセットに重み値を 割り当てることができます
BlendShapeWeightsSetの blendWeightIndexによって すべての重み値を更新します 元気な状態の植物を示す 「0」に設定します ここでは 植物が 元気な状態になるように設定します ここからしおれた状態に遷移させるため イージング関数を使用して この値を徐々に増減します
その後 新しい重み値を BlendShapeWeightsComponentに割り当てて 有効にします
コードを実行して これらの花を元気にしましょう
しおれていた花が 元気を取り戻しました ここで もう1台のロボットが 何をしているか見てみましょう
インバースキネマティクスにより ロボットが蝶に 手を伸ばしています また Animationアクションによって 蝶が飛び回っています これらは Reality Composer Proで タイムラインを使用して作成されています 蝶の位置を変更する カスタムコンポーネントも使用されています ロボットの頭部に注目すると 蝶の飛ぶ方向を 追っているのがわかります この動きを実現するには 新しいスケルトンポーズAPIを使用します 標準的なリグ付き3Dキャラクターは 相互接続されたボーンからなる スケルトン構造になっています 各ボーンは キャラクターまたは オブジェクトの それぞれ異なる部分に対応します
キャラクターをアニメーション化するには ジョイントを回転させて オブジェクトにポーズを取らせ 様々な位置に動かします この手法は キャラクターを歩かせたり 走らせたりするときによく用いられます
スケルトンポーズAPIは新しい SkeletalPosesComponentを追加します RealityKitによって生成される アニメーションを使用できます または インターフェイスを使用して 実行時にスケルトンを変更できます その場合 特定のジョイントについて プログラムで SkeletalPosesComponentに照会し トランスフォームを更新します
ここでは スケルトンポーズAPIを使用して ロボットのネックボーンを回転させ 飛び回る蝶を見ているかのような 動きを実現します
USDファイルから RealityKitにインポートされる スキンメッシュでは SkeletalPosesComponentが既に エンティティにアタッチされているので 初期化する必要はありません SkeletalPosesComponentを エンティティから取得するだけです 首関節の回転を更新します これはローカル空間であることに 注意してください フレームごとに首の回転を更新するには RealityKitの更新関数から このコードを呼び出します RealityKitは フレームごとに 登録済みの すべてのシステムで更新関数を呼び出します さらに 更新した値を コンポーネントにコミットします ジョイントトランスフォーム全体 または並行移動 スケール 回転のみを 更新できます 最大で毎フレーム更新できます コードを実行して 飛び回る蝶を目で追う ロボットの動作を確認しましょう
どうでしょう ワクワクしませんか?
このセッションで取り上げた内容を まとめましょう
Reality Composer Proの Timeline機能を使用してタイムライン上に アクションを配置し 初期化した後 トリガーによって再生する方法を説明しました 新しいフルボディインバースキネマティクスAPI を使用して ロボットがオブジェクトに 手を伸ばす動作を作成しました 組み込みとカスタムのアクションを使用して アクションのシーケンスを作成しました ブレンドシェイプアニメーションを使用して 異なる一連のシェイプを ブレンドする方法を見てきました ジョイントを回転させて オブジェクトを様々な位置に配置して ポーズ付けをし それらを アニメーション化する方法を見ました
これらの他にも 今年 Reality Composer Proには 数々のエキサイティングな機能が 追加されました ビデオドッキング iOSとmacOSへのデプロイ機能 そして visionOS 環境オーサリング ライティングなどです
これらのトピックについて詳しくは 次のセッションをご覧ください 「Enhance the immersion of media viewing in custom environments」 「Discover RealityKit APIs for iOS, macOS and visionOS」 これらの新機能はインタラクティブな 3Dコンテンツの作成に役立ちます 今後 皆さんが開発するアプリを 楽しみにしています ありがとうございました WWDCをお楽しみください
-
-
20:31 - Setup IKComponent
// Setup IKComponent import RealityKit struct HeroRobotRuntimeComponent: Component { var rig = try? IKRig(for: modelSkeleton) rig.maxIterations = 30 rig.globalFkWeight = 0.02 let hipsJointName = "root/hips" let chestJointName = "root/hips/spine1/spine2/chest" let leftHandJointName = "root/hips/spine1/spine2/chest/…/L_arm3/L_arm4/L_arm5/L_wrist" rig.constraints = [ .parent(named: "hips_constraint", on: hipsJointName, positionWeight: SIMD3(repeating: 90.0), orientationWeight: SIMD3(repeating: 90.0)), .parent(named: "chest_constraint", on: chestJointName, positionWeight: SIMD3(repeating: 120.0), orientationWeight: SIMD3(repeating: 120.0)), .point(named: "left_hand_constraint", on: leftHandJointName, positionWeight: SIMD3(repeating: 10.0)) ] let resource = try? IKResource(rig: rig) modelComponentEntity.components.set(IKComponent(resource: resource)) }
-
21:33 - Update IKComponent
// Update IKComponent import RealityKit struct HeroRobotRuntimeComponent: Component { guard let reachTarget = sceneRoot.findEntity(named: "reachTargetName") else { return } var reachPosition = reachTarget.position(relativeTo: entity) let time = sin(simTime) reachPosition.x += (20.0 + 50.0 * time) reachPosition.y += (40.0 + 30.0 * abs(time)) reachPosition.z += (20.0 + 20.0 * abs(time)) guard let ikComponent = modelComponentEntity.components[IKComponent.self] else { return } var reachPosition = reachTarget.position(relativeTo: entity) ... var leftHandConstraint = ikComponent.solvers.first?.constraints["left_hand_constraint"] leftHandConstraint?.target.translation = reachPosition // A blendValue = 0 means no influence on the constraint. // A blendValue = 1 means full influence on the constraint. var blendValue = isEnabled ? (time / totalBlendTime) : (1.0 - time / totalBlendTime) leftHandConstraint?.animationOverrideWeight.position = blendValue modelComponentEntity.components.set(ikComponent) }
-
24:36 - Sequence and play animation actions
// Play Animation Actions import RealityKit struct HeroRobotRuntimeComponent: Component { let rotateAnimationResource = createRotateAnimationResource() let walkAndMoveAnimationGroup = createWalkAndMoveAnimationGroup() let alignAtHomeActionResource = createAlignAtHomeActionResource() let robotTravelHomeCompleteActionResource = createRobotTravelHomeCompleteAction() // Build a sequence of the rotate, move and align animations/actions to play. let moveHomeSequence = try? AnimationResource.sequence(with: [rotateAnimationResource, walkAndMoveAnimationGroup, alignAtHomeActionResource, robotTravelHomeCompleteActionResource]) // Play the move-to-home sequence. _ = robotEntity.playAnimation(moveHomeSequence) }
-
25:59 - Setup EntityActions
// Setup EntityActions import RealityKit struct HeroRobotRuntimeComponent: Component { struct RobotMoveToHomeComplete: EntityAction { var animatedValueType: (any AnimatableData.Type)? { nil } } let travelCompleteAction = RobotMoveToHomeComplete() let actionResource = try! AnimationResource.makeActionAnimation(for: travelCompleteAction, duration: 0.1) let _ = robotEntity.playAnimation(actionResource) }
-
26:39 - EntityAction subscription
// EntityAction subscription import RealityKit struct HeroRobotRuntimeComponent: Component { // Subscribe to know when the EntityAction has started. RobotMoveToHomeComplete.subscribe(to: .started) { event in if event.playbackController.entity != nil { event.playbackController.stop() } } // Possible states of the robot. public enum HeroRobotState: String, Codable { case available … case arrivedHome } // Subscribe to know when the EntityAction has ended. RobotMoveToHomeComplete.subscribe(to: .ended) { event in if let robotEntity = event.playbackController.entity, var component = robotEntity.components[HeroRobotRuntimeComponent.self] { component.setState(newState:.arrivedHome) } } }
-
29:17 - Setup BlendshapeWeightsComponent
// Setup BlendShapeWeightsComponent import RealityKit struct HeroPlantComponent: Component, Codable { guard let modelComponentEntity = findModelComponentEntity(entity: entity), let modelComponent = modelComponentEntity.components[ModelComponent.self] else { return } let blendShapeWeightsMapping = BlendShapeWeightsMapping(meshResource: modelComponent.mesh) // Create the blend shape weights component. entity.components.set(BlendShapeWeightsComponent(weightsMapping: blendShapeWeightsMapping)) }
-
29:38 - Update BlendshapeWeightsComponent
// Update BlendShapeWeightsComponent struct HeroPlantComponent: Component, Codable { guard let component = entity.components[BlendShapeWeightsComponent.self] else { return } var blendWeightSet = blendShapeComponent.weightSet // Update the weights in the BlendShapeWeightsSet for weightIndex in 0..<blendWeightSet[blendWeightsIndex].weights.count { blendWeightSet[blendWeightsIndex].weights[weightIndex] = 0.0 } // Assign the new weights to the blend shape component. for index in 0..<blendWeightSet.count { component?.weightSet[blendWeightsIndex].weights = blendWeightSet[index].weights } }
-
32:01 - Setup and update Skeletal Poses
// Update Skeletal Poses import RealityKit struct StationaryRobotRuntimeComponent: Component { guard var component = entity.components[SkeletalPosesComponent.self] else { return } let neckRotation = calculateRotation() component.poses.default?.jointTransforms[neckJointIndex].rotation = neckRotation }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。