起因

最近要用 Timeline 对游戏的进程进行编排,需要给游戏中的字幕添加动态的效果

有关 Timeline 的自定义功能介绍的相关资料较少,花了一段时间才搞清除创建一个自定义的 Clip 需要额外定义4个脚本

实现效果

iShot2020-08-04上午10.26.39

具体实现

Track

首先创建一个 TextSwitcherTrack.cs 文件

这个是 Timeline 中的 Track 轨道,在轨道中绑定对应的 Text 组件对象

[TrackClipType(typeof(TextSwitcherClip))]
[TrackBindingType(typeof(Text))]
public class TextSwitcherTrack : TrackAsset {
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) {
        return ScriptPlayable<TextSwitcherMixerBehaviour>.Create(graph, inputCount);
    }
}

Track 脚本写好后,我们就可以在 Timeline 面板上添加对应的 Text Track

Behaviour

接着创建 Text 的行为脚本

这里的属性,就是我们在 Clip 中填写的属性

[Serializable]
public class TextSwitcherBehaviour : PlayableBehaviour {
    public Color  color    = Color.white;
    public int    fontSize = 14;
    public string text;
}

Clip

接着创建对应的 Clip 脚本

创建好 Clip 脚本之后,就可以在 Track 中新增 Clip 片段了

[Serializable]
public class TextSwitcherClip : PlayableAsset, ITimelineClipAsset {
    public TextSwitcherBehaviour template = new TextSwitcherBehaviour();

    public ClipCaps clipCaps => ClipCaps.Blending;

    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner) {
        var playable = ScriptPlayable<TextSwitcherBehaviour>.Create(graph, template);
        return playable;
    }
}

Mixer

到这里我们还没有对 Text 组件进行赋值,因为 Timeline 是以 Track 为单位的,如果在 BehaviourProcessFrame 函数中对 Text 进行赋值时,当我们移动 Clip,在间隙中会导致我们不希望显示 Text 时,会错误显示

这时候就需要用 MixerTrack 进行控制,我们可以在这里拿到所有的 Clip,对其进行管理

public class TextSwitcherMixerBehaviour : PlayableBehaviour {
    private Color  _defaultColor;
    private int    _defaultFontSize;
    private string _defaultText;
    private float  _currentAlpha;

    private Text _trackBinding;

    public override void ProcessFrame(Playable playable, FrameData info, object playerData) {
        _trackBinding = playerData as Text;

        _defaultText  = "";
        _currentAlpha = 0f;

        if(!_trackBinding){ return; }

        int inputCount = playable.GetInputCount();

        for(int i = 0; i < inputCount; i++){
            float inputWeight = playable.GetInputWeight(i);
            if(inputWeight > 0f){
                ScriptPlayable<TextSwitcherBehaviour> inputPlayable =
                    (ScriptPlayable<TextSwitcherBehaviour>) playable.GetInput(i);
                TextSwitcherBehaviour input = inputPlayable.GetBehaviour();
                _defaultText     = input.text;
                _defaultFontSize = input.fontSize;
                _defaultColor    = input.color;
                _currentAlpha    = inputWeight;

                _defaultColor.a = _currentAlpha;
            }
        }

        _trackBinding.text     = _defaultText;
        _trackBinding.fontSize = _defaultFontSize;
        _trackBinding.color    = _defaultColor;
    }

    public override void OnPlayableDestroy(Playable playable) {
        if(_trackBinding == null){ return; }

        _trackBinding.color    = _defaultColor;
        _trackBinding.fontSize = _defaultFontSize;
        _trackBinding.text     = _defaultText;
    }
}

What doesn’t kill you makes you stronger.