こんにちは、Unityエンジニアのオオバです。

Unity2018.2.12f1時点ではExperimentalな機能である
TransformSceneHandleについて調査しました。

リファレンスはコチラ

Position, rotation and scale of an object in the scene.
A TransformSceneHandle is a safe handle on a TransformAccess. The Animator used to create this handle manages the validity of this handle.

 シーン内のオブジェクトの位置、回転、スケール。  
 TransformSceneHandleは、TransformAccessの安全なハンドルです。  
 このハンドルの作成に使用されたアニメーターは、  
 このハンドルの有効性を管理します。  

とリファレンスでは説明されています。

調査するとTransformSceneHandleは
AnimationJobを使用する際のTransformへのアクセス方法であることがわかりました。
※IJobParallelForTransformにおけるTransformへのアクセスは、TransformAccessを使用していました

TransformSceneHandleについて調べていたら、AnimationJobについて調べていた件_0

簡単な図にするとこうなります。

  1. TransformSceneHandleはAnimatorとTransformを元に生成
  2. TransformSceneHandleをAnimationJobへ渡す
  3. AnimationJob内でTransformSceneHandleへ位置回転データを渡す
  4. AnimationJobはAnimationScriptPlayableを通して再生される

というような流れになります。

AnimationJobはIAnimationJobインターフェース
実装する必要があり、以下のような実装になっています。

public interface IAnimationJob  
{
  /// <summary>  
  ///   <para>Defines what to do when processing the animation.</para>  
  /// </summary>  
  /// <param name="stream">The animation stream to work on.</param>  
  void ProcessAnimation(AnimationStream stream);  

  /// <summary>  
  ///   <para>Defines what to do when processing the root motion.</para>  
  /// </summary>  
  /// <param name="stream">The animation stream to work on.</param>  
  void ProcessRootMotion(AnimationStream stream);  
}

AnimationJob内の処理ProcessAnimation
ProcessRootMotionの引数でAnimationStreamが渡ってきます。

このAnimationStreamインスタンスを通して、
TransformSceneHandleの値を更新していきます。

TransformSceneHandleについて調べていたら、AnimationJobについて調べていた件_1

準備として、AnimatorのコンテキストメニューからBuild Generic Avatarを実行。

TransformSceneHandleについて調べていたら、AnimationJobについて調べていた件_2

このようにAvatarがセットされている必要があります。

リファレンス記載の以下のサンプルを動かしてみます。

💻ソースコード : TransformSceneHandleExample.cs
using Unity.Burst;  
using UnityEngine;  
using UnityEngine.Playables;  
using UnityEngine.Animations;  
using UnityEngine.Experimental.Animations;  

[BurstCompile]  
public struct TransformSceneHandleJob : IAnimationJob  
{
    public TransformSceneHandle handle;  
    public Vector3 position;  
    public Vector3 rotation;  
    public Vector3 scale;  

    public void ProcessRootMotion(AnimationStream stream)  
    {
        // Set the new local position.  
        handle.SetLocalPosition(stream, position);  

        // Set the new local rotation (converted from euler).  
        handle.SetLocalRotation(stream, Quaternion.Euler(rotation));  

        // Set the new local scale.  
        handle.SetLocalScale(stream, scale);  
    }

    public void ProcessAnimation(AnimationStream stream){}  
}

[RequireComponent(typeof(Animator))]  
public class TransformSceneHandleExample : MonoBehaviour  
{
    public Transform sceneTransform;  
    public Vector3 position;  
    public Vector3 rotation;  
    public Vector3 scale = Vector3.one;  

    PlayableGraph m_Graph;  
    AnimationScriptPlayable m_AnimationScriptPlayable;  

    void Start()  
    {
        if (sceneTransform == null)  
            return;  

        var animator = GetComponent<Animator>();  

        m_Graph = PlayableGraph.Create("TransformSceneHandleExample");  
        var output = AnimationPlayableOutput.Create(m_Graph, "output", animator);  

        var animationJob = new TransformSceneHandleJob();  
        animationJob.handle = animator.BindSceneTransform(sceneTransform);  
        m_AnimationScriptPlayable = AnimationScriptPlayable.Create(m_Graph, animationJob);  

        output.SetSourcePlayable(m_AnimationScriptPlayable);  
        m_Graph.Play();  
    }

    void Update()  
    {
        if (sceneTransform == null)  
            return;  

        var animationJob = m_AnimationScriptPlayable.GetJobData<TransformSceneHandleJob>();  
        animationJob.position = position;  
        animationJob.rotation = rotation;  
        animationJob.scale = scale;  
        m_AnimationScriptPlayable.SetJobData(animationJob);  
    }

    void OnDisable()  
    {
        if (sceneTransform == null)  
            return;  

        m_Graph.Destroy();  
    }
}

結果並列で処理される

TransformSceneHandleについて調べていたら、AnimationJobについて調べていた件_3

実行すると上のキャプチャのように並列処理されていました。

アセンブリは表示されず

先日「アセンブリやるぜ!!」っていう記事を書きました。
初心者向け事前知識無しの状態から、Mac環境でUnityエンジニアがアセンブリに触れてみる

AnimationJobをBurstCompile。
そのアセンブリがどうなっているかを確認しようとしました。

AnimationJobがExperimentalのせいか、
BurstInspectorにアセンブリが表示されませんでした。

コンソールを確認すると以下のように
AnimationJobのBurstCompile自体は走っているようです。

While compiling job: System.Void UnityEngine.Experimental.Animations.  
ProcessAnimationJobStruct`1::ExecuteProcessRootMotion(  
T&,System.IntPtr,System.IntPtr,Unity.Jobs.LowLevel.Unsafe.JobRanges&,System.Int32)  

BurstCompileのログを出力する方法

TransformSceneHandleについて調べていたら、AnimationJobについて調べていた件_4

メニューのJobs > Show Burst Timings
チェックを入れるとBurstCompilerのコンパイルタイミングで、
ログが表示されるようになります。

気になる方は確認してみてください。

まとめ

TransformSceneHandleはAnimationJobで使用されます。
※どちらもExperimentalなので注意

オススメ記事
検証環境
参考サイト