Unity Timeline "singal" と "marker" の違い

tsubakit1.hateblo.jp

使用しているunity version

unity2019.2.17 timeline ver 1.1.0

signal, markerとは

公式マニュアル docs.unity3d.com

Timelineの時間軸にイベントを設定し、何かしらの処理を実行したいという事をネット上で調べると、signalかmarkerを使うようだ、という事が分かります。

Animation Clipで使われるEventのTImeline版のようなイメージです。 docs.unity3d.com

どっちが良いのか

先に結論を行ってしまうと、signalはmarkerを拡張したもので、markerに幾つかの便利な機能を付けたものがsignalという感じです。 ですのでイベントを仕込みたいだけであれば、基本signalで問題ないかと思われます。

ただ、プロジェクトによっては不要な機能も一緒に付いてくるかもしれないので、違いを把握して検討するのが良さそうです。 では素の状態での違いを見ていきます。

共通の仕様

設定できるエリアが2箇所あります。 f:id:ssr_maguro:20200123162053p:plain

  • timelineのmarkerエリア、またはtrackエリアを右クリックで設定します。

  • Markerエリアで設定した場合、PlayableDirecterコンポーネントが設定されたgameObjectがイベントを受け取る

  • Trackエリアで設定した場合、Trackの対象として設定されたgameObjectがイベントを受け取る
    • INotificationを実装したMonoBehaviorがMarkerイベントを受け取る事ができる
    public void OnNotify(Playable origin, INotification notification, object context)
    {
        CustomAaaSignal aaaSignal = notification as CustomAaaSignal;
        if (aaaSignal != null)
        {
            Instantiate(aaaSignal.prefab);
        }
    }

markerの仕様

docs.unity3d.com

  • イベントを発火するMarkerクラスを作成
  • singalと違い、イベントに対応したアセットは不要
  • Runtime, Editor両方でイベントが発生する
  • 再生ヘッドがmarkerイベントよりも後に飛ばされてもイベントは発行される

signalの仕様

docs.unity3d.com ドキュメントを見ると、Markerの派生クラスがSignalEmitterですね。

signalのイベントの受け取り方には2通りあります。

1. INotificationを実装したMonoBehaviorを用意する

markerと同じ方法です。

  • INotificationを実装したMonoBehaviorがOnNotifyでイベントを受け取る事ができる f:id:ssr_maguro:20200123175401p:plain
  • signalアセットは必ずしも必要ではない。

2. SignalRecieverを使い、inspecterで設定

  • イベントを区別するためのsignalアセットを作る
  • signalのイベントを受け取るgameObjectにSignalReciever (unityが用意しているコンポーネント)をadd componentする
  • 受け取ったsingalとgameObjectの関数を紐付ける処理を、受け取る側のgameObjectに付けられたSignalRecieverで設定する

SignalReciever f:id:ssr_maguro:20200123191101p:plain

個人的にプログラムで完結したいと考えているので、僕が実装するなら①の方を使うと思います。 というかSignalRecieverを使うとsignalで設定した引数が受け取れないので、値を受け取りたい場合は①しか選択肢がないですね。

引数を渡したい

素のmarker、signalではイベントが起きた事しか通知出来ません。 イベント毎にidを渡したり、文字列を渡したりしたい場合、marker、signalを拡張したクラスを作成します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;

public class CustomAaaSignal : SignalEmitter
{
    public string text;
    public GameObject prefab;
}

f:id:ssr_maguro:20200123174712p:plain

inspecterで設定できるようになりました。

f:id:ssr_maguro:20200123174841p:plain

その他のオプション

ここはmarkerに無いので、素のmarkerだと不便な点です。

f:id:ssr_maguro:20200123162551p:plain

Retroactiveフラグ
  • シークされて再生ヘッドがsingalを飛ばしても、遡ってイベントを発行するか? というRetroactiveフラグがある (timeline ver1.0.0ではRetroactiveフラグに関係なく、必ず発行されていました。。バグですね。)
EmitOnceフラグ
  • loopする場合も一回しかイベントを発生させない、というEmitOnceフラグがある

f:id:ssr_maguro:20200123163622p:plain

ただ、これらは簡単に実装可能です。 INotificationOptionProviderを継承・実装します。 下記はSignalの実装をそのまま持ってきたものです。

using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[System.Serializable]
public class TimelineSmapleMarker : Marker, INotification, INotificationOptionProvider
{
    [SerializeField] bool m_Retroactive;
    [SerializeField] bool m_EmitOnce;

    PropertyName INotification.id
    {
        get { return new PropertyName(string.Empty); }
    }

    public bool retroactive
    {
        get { return m_Retroactive; }
        set { m_Retroactive = value; }
    }

    public bool emitOnce
    {
        get { return m_EmitOnce; }
        set { m_EmitOnce = value; }
    }

    NotificationFlags INotificationOptionProvider.flags
    {
        get
        {
            return (retroactive ? NotificationFlags.Retroactive : default(NotificationFlags)) |
                   (emitOnce ? NotificationFlags.TriggerOnce : default(NotificationFlags)) |
                   NotificationFlags.TriggerInEditMode;
        }
    }
}

割と簡単にRetroactive、EmitOnceフラグは実装できました。

Runtimeだけなのか、Editorでも発生させるのか? を選べる

これはSignalRecieverを使う必要があります。 markerだとRuntime、Editor両方で発生します。

SignalRecieverの中身を見ると、いったんUnityEventに変換されています。 UnityEventはRuntimeだけなのか、Editorでも発生させるのかのフラグがあるため、それを使っているようです。

これら踏まえて、SignalAssetもSignalRecieverも不要ということであれば、markerを使ってカスタマイズしていくのを検討して良さそうです。

その他の参考URL

markerの拡張方法が紹介されています。 blogs.unity3d.com