はじめに

こんにちは!システム開発部のYです。

visionOS 2.0では、新機能としてTimelineアニメーションが追加されました。
これは、Reality Composer Proに導入された機能で、特定の順序やタイミングでアクションをシーケンス化できるものです。
アクションの編集や設定も直感的に行えるようになっています。

今回は、モデルの追加からアニメーションの実行、そしてSwift側でのイベント検知までの流れをまとめていきたいと思います。

環境

  • Swift Version: 5.10
  • Xcode: 16.1
  • visionOS: 2.0
  • Reality Composer Pro: 2.0
  • macOS: 14.5

実装について

Timelineアニメーションを使用し、回転と移動を行います。
さらに、移動完了をSwift側で検知する方法についても解説します。

今回は、以下の手順で進めていきます。

3Dモデルの準備

今回は、ニコニ立体ちゃんを使用します。

1. モデルのダウンロード
ニコニ立体から、ニコニ立体ちゃんをダウンロードします。
ニコニ立体ちゃん (VRM)

2. VRMからUSDZに変換
VRM形式なので、USDZに変換する必要があります。

  1. Blenderを使用してFBXに変換
    VRMファイルはそのままではBlenderにインポートできないため、以下のサイトを参考にアドオンのインストールを行います。
    アドオンのインストール

    その後、VRMファイルをBlenderにインポートし、FBX形式でエクスポートします。
  2. アニメーションの取り込み
    Mixamoを開き、「UPLOAD CHARACTER」でエクスポートしたFBXをアップロードします。

    アクションに「Idle」を選択し「DOWNLOAD」を選択しダウンロードします。

  3. Reality Converterでアニメーション付きFBXをUSDに変換する手順
    • FBXの読み込みとテクスチャの設定
      • Reality Converterを開き、ダウンロードしたFBXファイルを読み込みます。
      • 各マテリアルにテクスチャを設定します。
        ※テクスチャは、VRMEditorTools を使用して取得できます。
    • USDZ形式での書き出し
      • Reality Converterのメニューから、[ファイル] > [書き出す] を選択します。


  4. 歩きアニメーションの作成
    • アニメーションの追加
      • アニメーションを切り替えられるようにするため、2つ目のアニメーションを取り込んだUSDZを作成します。
    • FBXの取り込みとアニメーションの設定
      • 手順1で作成したFBXファイルをMixamoに再度取り込みます。
      • 歩きアニメーションを選択し、FBXをダウンロードします。
        手順1で作成したFBXをMixamoで再度取り込み、歩きアニメーションをするUSDZを作成します。
    • USDZの作成
      • ダウンロードしたFBXをReality Converterに取り込み、歩きアニメーション付きのUSDZを作成します。

        ※モデルに複数のアニメーションを入れる事が現状できず、アニメーションの数だけUSDZを用意する必要があります。

モデルの配置、設定

プロジェクトの作成を行い、RealityComposer Proを開きます。
保存したIdleアニメーションがあるUSDZを読み込み、シーンに配置します。

AnimationLibraryに、Idleアニメーションがすでに存在していることがわかります。

AnimationLibraryに、歩きアニメーションを追加したいのでAnimationLibraryの左下「+」ボタンから歩きアニメーションが入っているUSDZを選択します。

AnimationLibraryに、歩きアニメーションも追加されていることがわかります。

歩かせるためのTarget用のCubeを、ニコニ立体ちゃんの近くに設置します。

TimeLineアニメーション

Reality Composer Proに追加された新しい機能で、特定の順序やタイミングで実行するアクションをシーケンス化することができます。
これらのアクションを簡単に編集し、設定することが可能です。

  • TimeLinesタブ(画像赤枠)
    アニメーションの設定は、Reality Composer ProのTimeLinesタブで行います。
  • TimeLines(画像緑枠)
    全てのタイムラインがリストで表示されています。
  • タイムラインエディタ(画像青枠)
    中央にメインのタイムラインエディタがあります。
    右側にあるアクションを設定してタイムライン上に並べることができます。
  • Actions(画像紫枠)
    全てのビルドインアクションがリスト表示されています。
    これらをタイムラインエディタにドラッグ&ドロップすることができます。

Actionsで設定可能な項目
以下に設定できるActionを一覧で説明します。

  • Add Force
    オブジェクトに物理的な力を加え、移動させます。
  • Animation
    AnimationLibraryからアニメーションを再生します。
  • Billboard
    エンティティが常にカメラの方向を向くようにアニメーションします。
  • Disable Entity
    対象のエンティティを即座に非表示にし、操作やアニメーションが無効化されます。
    再操作する場合は「Enable Entity」を使用します。
  • Emphasize
    エンティティに組み込みの強調アニメーションを適用し、跳ねたり拡大縮小する動きで目立たせます。
  • Enable Entity
    エンティティを表示・操作可能にします。
    非表示にしたエンティティを再び動作させる場合に使用します。
  • Hide
    対象のエンティティの不透明度(Opacity)をアニメーションで下げて見えなくします。
    消失速度やタイミングの調整が可能です。
  • Notification
    カスタム通知を発行し、コード内で受信・処理できます。
    特定のイベント発生時に他の処理や動作をトリガーします。
  • Orbit
    エンティティを別のエンティティの周囲で回転させます。
  • Play Audio
    エンティティから音声クリップを再生します。
    特定の場所やオブジェクトから音を出すことができます。
  • Replace Behaviors
    Behaviorがトリガーされたときに再生されるタイムラインを変更する機能です。
    これにより、既存の動作を別の動作に置き換えることができます。
  • Show
    エンティティの不透明度(Opacity)を100%にアニメーションで変更して表示させます。
  • Spin
    対象のエンティティを回転させます。
  • Transform By
    現在の位置・回転・スケールを基準に、エンティティを相対的にアニメーションで変化させます。
  • Transfrom To
    指定した位置・回転・スケールにエンティティをアニメーションで移動・変形させます。

TimeLineアニメーションの実装

まず左側の、TimeLinesの左下「+」ボタンから新しいタイムラインを作成します。
名前を、「AliciaSolidCubeMoveAnimation」とします。

回転させる

回転させるには、Spinアクションを使用します。
右側のActionsからSpinをタイムラインエディタに追加します。
次にSpinを選択し、右側のインスペクタパネルで以下の設定を行います。

  • Target: Chooseから、回転させたい「ニコニ立体ちゃん」を選択します。
  • Revolutions: 左向きに回転したいので、おおよそ「0.25」を設定します。

移動させる

移動させるために、Transform_Toアクションを使用します。
右側のActionsからTransform_Toをタイムラインエディタに追加します。
次にTransform_Toを選択し、右側のインスペクタパネルで以下の設定を行います。

  • Target: Chooseから、回転させたい「ニコニ立体ちゃん」を選択します。
  • Position: 左に移動させたいのでxに、「-5m 」を設定します。
  • Timing Function: 「Linear」を選択します。

キャラクターアニメーションの設定

アニメーションを設定するには、Animation アクションを使用します。

待機アニメーションの追加

右側のActionsからAnimationをタイムラインエディタに追加します。
同一時間で再生させたいので、SpinやTransform_Toの下に配置します。

  • Target: Chooseから、回転させたい「ニコニ立体ちゃん」を選択します。
  • Animation: default subtree Animation(Idle)を選択します。
  • Repeat Forever: チェックを入れます。

移動アニメーションの追加

右側のActionsからAnimationをタイムラインエディタに追加します。
同一時間で再生させたいので、SpinやTransform_Toの下に配置します。

  • Target: Chooseから、移動させたい「ニコニ立体ちゃん」を選択します。
  • Animation: AliciaSolid Walking(Walking)を選択します。
  • Repeat Count: 移動距離と合わせるため「3」を入力します。

TimelineからTimelineを呼び出す

移動アニメーションを汎用的に使用したい場合があると思います。
その場合はTimeline毎に分割させます。
Timelineを新しく作り移動アニメーションを移動させます。

左側のTimeLinesタブにある左下の「+」ボタンから新しいタイムラインを作成します。
名前を「AliciaSolidWalk」とします。

以下のように、移動アニメーションのみを移動させます。

作成したTimeline「AliciaSolidWalk」を、AliciaSolidCubeMoveAnimationのタイムラインエディタにドラッグ&ドロップします。配置後、再生時間を調整してください。

これで設定は完了です。再生ボタンを押すと、回転して移動するアニメーションが確認できます。

イベント検知

イベントを検知するためには、Notificationアクションを使用します。
右側のActionsからNotificationをタイムラインエディタに追加します。
移動完了のタイミングを検知させたいので、「Transform_To」の後に配置します。

  • Target: Chooseから、「Cube」を選択
  • Identifier: 「MoveComplete」と入力します。※Identifierが、コード側で判別する値になります。

アニメーションの実行設定

アニメーションを実行したいターゲットを選択します。
今回は Cube を選択し、Cube に Add Component をクリックして 「Behaviors」 コンポーネントを追加します。
Behaviors には以下の4つの項目があり、それぞれのタイミングでアクションを実行できます。

  • On Tap: オブジェクトをタップした際に実行
  • On Collision: オブジェクトが別のオブジェクトと衝突した際に実行
  • On Added To Scene: オブジェクトがシーンに追加されたタイミングで実行
  • On Notification: コード側から NotificationCenter を使って任意のタイミングで実行

今回はタップ時にアニメーションを実行するため、+ を選択し、On Tapを選びます。次に、Actionで実行したいアクションを選択します。

ImmersiveView.swiftの変更

applyTapForBehaviorsの実行

tapGestureを実装し、tapしたentityのapplyTapForBehaviorsを実行するのみです。

import SwiftUI
import RealityKit
import RealityKitContent

struct ImmersiveView: View {

    var body: some View {
        RealityView { content in
            // Add the initial RealityKit content
            if let immersiveContentEntity = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
                content.add(immersiveContentEntity)
            }
        }
        .gesture(tapGesture)
    }
    
    var tapGesture: some Gesture {
        TapGesture()
            .targetedToAnyEntity()
            .onEnded { value in
                // RealityComposerProで、設定されているBehaviorsを実行する
                _ = value.entity.applyTapForBehaviors()
            }
    }
}

実行し、Cubeをタップするとアニメーションが実行される事を確認できます。

アニメーション完了の検知

NotificationCenter を使用し、onReceive で通知を検知することができます。
通知の情報は、output.userInfo?["RealityKit.NotificationTrigger.Identifier"] を使用して取得でき、ここで取得した値が Timeline で設定した NotificationIdentifier になります。

import SwiftUI
import RealityKit
import RealityKitContent

struct ImmersiveView: View {
    
    private let notificationTrigger = NotificationCenter.default.publisher(for: Notification.Name("RealityKit.NotificationTrigger"))
    
    var body: some View {
        RealityView { content in
            // Add the initial RealityKit content
            if let immersiveContentEntity = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
                content.add(immersiveContentEntity)
            }
        }
        .gesture(tapGesture)
        .onReceive(notificationTrigger) { output in
            guard let notificationName = output.userInfo?["RealityKit.NotificationTrigger.Identifier"] as? String else {
                return
            }
            
            // notificationNameによって、処理を分岐させる
            switch notificationName {
            case .moveCompleteNotification:
                // 移動先に、移動完了したことを検知(TimeLineから指定している)
                print("MoveComplete")
            default:
                break
            }
        }
    }
    
    var tapGesture: some Gesture {
        TapGesture()
            .targetedToAnyEntity()
            .onEnded { value in
                // RealityComposerProで、設定されているBehaviorsを実行する
                _ = value.entity.applyTapForBehaviors()
            }
    }
}

private extension String {
    static let moveCompleteNotification = "MoveComplete"
}

まとめ

TimeLineアニメーションの使い方についてまとめました。
回転や移動など、一連の動作が決まっているアニメーションを作成する際に、とても便利な機能です。
しかし、Swift側から特定のアニメーションを変更する等カスタマイズは出来なさそうです。
その場合は、アニメーションを1から作成する必要があります。
この記事がお役に立てば幸いです。本記事について誤りや改善点があれば、是非ご指摘ください。

参考



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる