LoginSignup
13
6

More than 5 years have passed since last update.

[Unity][Timeline] 独自実装のトラック上で生成されるクリップのデフォルトの長さを設定する

Last updated at Posted at 2018-10-10

はじめに

※このサンプルはUnity2018.3.0b4で動作確認しています
DefaultPlayablesのサンプル実装のようにトラックを独自実装する場合、用途によってはデフォルトで生成されるクリップの長さを変更したい場合があります

しかし、サッと公開されている情報を漁ってみてもどのように設定するのかイマイチ分かりません
ここは内部実装を覗いてみて何か手段がないか探ってみることにしましょう

内部実装の解析

早速クリップを生成する内部コードを掘り起こして確認すると、下記のような恐ろしい決め打ち実装がなされていて、そのままでは強制的に5秒でクリップが生成されてしまうような設計になっています

public class TimelineClip : ISerializationCallbackReceiver
{
    // ...(省略)
    public static readonly float kDefaultClipDurationInSeconds = 5f;
    // ...(省略)
}
public abstract class TrackAsset : PlayableAsset, IPropertyPreview, ISerializationCallbackReceiver
{
    // ...(省略)
    internal TimelineClip CreateNewClipContainerInternal()
    {
        // ...(省略)
        timelineClip.duration = (double) TimelineClip.kDefaultClipDurationInSeconds;
        // ...(省略)
        return timelineClip;
    }
    // ...(省略)
}

しかし、もう少し内部実装を追ってみるととある項目を見てクリップ生成時の長さの設定を上書きしている箇所を発見することができます

public abstract class TrackAsset : PlayableAsset, IPropertyPreview, ISerializationCallbackReceiver
{
    // ...(省略)
    private TimelineClip CreateClipFromAsset(ScriptableObject playableAsset)
    {
        TimelineClip containerInternal = this.CreateNewClipContainerInternal();
        // ...(省略)
        IPlayableAsset playableAsset1 = playableAsset as IPlayableAsset;
        if (playableAsset1 != null)
        {
            // なんかここでIPlayaleAsset.durationを使ってTimelineClip.durationを設定しているぞ(ΦωΦ)!
            double duration = playableAsset1.duration;
            if (!double.IsInfinity(duration) && duration > 0.0)
                containerInternal.duration = Math.Min(Math.Max(duration, TimelineClip.kMinDuration), TimelineClip.kMaxTimeValue);
        }
        // ...(省略)
    }
    // ...(省略)
}

結論

IPlayableAsset はトラックを独自実装する際にTrackClipTypeAttributeで設定するクラスが継承することになります
継承関係としてはIPlayableAsset -> PlayableAsset -> 独自PlayableAsset という形になり、独自実装側で何もしない場合durationプロパティはPlayableAssetdouble.PositiveInfinityが定義されることになっています

よってこのdurationプロパティを独自実装側でさらにoverrideしてあげて、任意のデフォルト時間を設定してあげれば、クリップが生成されたときの長さを自由に設定できるようになるということになります

最後に、実際にデフォルトのクリップの長さを変更した実装例を紹介して終わりにしたいと思います

サンプルコード

トラックの独自実装の一連の流れを下記に全て並べてみていますが、重要なのはSamplePlayableAssetクラスでoverrideしているdouble durationプロパティです

SamplePlayableAsset.cs
// クリップ単位で持つPlayableAsset
public class SamplePlayableAsset : PlayableAsset, ITimelineClipAsset
{
    const double DefaultDuration = 1.0f;

    [SerializeField] float num;

    public float Num
    {
        get { return num; }
    }

    // IPlayableAsset.durationを実装するとその値をデフォルト値としてTimelineClipを生成してくれる
    public override double duration
    {
        get { return DefaultDuration; }
    }

    public ClipCaps clipCaps
    {
        get { return ClipCaps.Blending; }
    }

    // クリップ単位のPlayableを生成する
    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    {
        // 自身(PlayableAsset)の情報を持たせたBehaviourでScriptPlayableを生成する
        // ※これによりProcessFrame内でPlayableからBehaviourを取り出し、クリップ単位のPlayableAsset情報にアクセスできるようになる
        return ScriptPlayable<SampleBehaviour>.Create(graph, new SampleBehaviour(this));
    }
}
SampleBehaviour.cs
// クリップ単位の処理する
public class SampleBehaviour : PlayableBehaviour
{
    // ここではミキサー側からPlayableAsset情報を取り出すためのプロパティを定義しているだけになっている
    public SamplePlayableAsset Asset { get; private set; }

    public SampleBehaviour(SamplePlayableAsset asset)
    {
        Asset = asset;
    }

    public SampleBehaviour()
    {
    }
}
SampleTrack.cs
// 独自実装のトラック
[TrackClipType(typeof(SamplePlayableAsset))]
[TrackBindingType(typeof(SampleComponent))]
public class SampleTrack : TrackAsset
{
    // クリップ全体を処理できるミキサーの生成
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        return ScriptPlayable<SampleMixer>.Create(graph, inputCount);
    }
}
SampleMixer.cs
// クリップ全体を処理するためのミキサー
public class SampleMixer : PlayableBehaviour
{
    // 現在のフレームの処理を実装する
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        var component = playerData as SampleComponent;
        if (component == null)
        {
            return;
        }

        // ここらへんはクリップごとにミキサーがブレンド処理をしている風を装っているだけなので深い意味はない
        var num = 0.0f;
        var inputCount = playable.GetInputCount();
        for (var i = 0; i < inputCount; ++i)
        {
            var weight = playable.GetInputWeight(i);
            if (Mathf.Approximately(weight, 0))
            {
                continue;
            }

            var input = (ScriptPlayable<SampleBehaviour>) playable.GetInput(i);
            var behaviour = input.GetBehaviour();
            var asset = behaviour.Asset;

            num += (asset.Num * weight);
        }

        component.Num = num;
    }
}
SampleComponent.cs
// 独自トラックにBindする対象となるコンポーネント
public class SampleComponent : MonoBehaviour
{
    [SerializeField] float num;

    // ミキサーから設定される想定のプロパティ
    public float Num
    {
        get { return num; }
        set { num = value; }
    }

    void Update()
    {
        // とりあえzyミキサーから設定されたパラメータをデバッグログ出力している
        Debug.LogFormat("num={0}", Num);
    }
}
13
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6