Timeline - PlayableBehavior轨道

实现CanvasGroup的补间动画

1) 轨道片段数据

using UnityEngine;
using UnityEngine.Playables;

[System.Serializable]
public class CanvasGroupClip : PlayableAsset
{
    [SerializeField]
    private ExposedReference<CanvasGroup> _target;
    [SerializeField]
    private float _startValue;
    [SerializeField]
    private float _endValue;

    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
        var playable = ScriptPlayable<CanvasGroupBehaviour>.Create(graph);
        var canvasGroup = _target.Resolve(graph.GetResolver());
        playable.GetBehaviour().Init(canvasGroup, _startValue, _endValue);

        return playable;
    }

}

2) 轨道Clip逻辑

using UnityEngine;
using UnityEngine.Playables;

[System.Serializable]
public class CanvasGroupBehaviour : PlayableBehaviour
{
    public CanvasGroup _canvasGroup;
    public float _startValue;
    public float _endValue;

    public void Init(CanvasGroup canvasGroup, float startValue, float endValue)
    {
        _canvasGroup = canvasGroup;
        _startValue = startValue;
        _endValue = endValue;
    }

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        if (null == _canvasGroup)
            return;
            
        float progress = (float)(playable.GetTime() / playable.GetDuration());
        float alpha = Mathf.Lerp(_startValue, _endValue, progress);
        _canvasGroup.alpha = alpha;
        Debug.Log($"CanvasGroupBehaviour: alpha: {alpha}");
    }

}

创建一个Playable轨道

PlayableGraph的可视化图形:

 

更加优雅的写法

上面的代码_startValue, _endValue需要在两个类中都写一遍,还得给他们赋值,下面的代码写法只需要在Behaviour中写一遍

using UnityEngine;
using UnityEngine.Playables;

[System.Serializable]
public class CanvasGroupClip : PlayableAsset
{
    public CanvasGroupBehaviour template = new CanvasGroupBehaviour();

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

}

#

using UnityEngine;
using UnityEngine.Playables;

[System.Serializable]
public class CanvasGroupBehaviour : PlayableBehaviour
{
    [SerializeField]
    private ExposedReference<CanvasGroup> _target;
    [SerializeField]
    public float _startValue;
    [SerializeField]
    public float _endValue;

    private CanvasGroup _canvasGroup;

    public override void OnGraphStart(Playable playable)
    {
        _canvasGroup = _target.Resolve(playable.GetGraph().GetResolver());
    }

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        if (null == _canvasGroup)
            return;

        float progress = (float)(playable.GetTime() / playable.GetDuration());
        float alpha = Mathf.Lerp(_startValue, _endValue, progress);
        _canvasGroup.alpha = alpha;
        Debug.Log($"CanvasGroupBehaviour: alpha: {alpha}");
    }

}

#

 

关于为什么对象绑定需要用到ExposedReference

一份timeline资源,可以用于多个不同的地方,只要绑定不同的对象就行了,而直接使用public CanvasGroup _target;是无法做到这样的功能的,

所以就引入了ExposedReference,他需要根据使用的地方解析出绑定对象

 

timeline资源的对象绑定关系没有直接保存在.playable资源中,而是保存在场景或预制体的PlayableDirector的Bindings下的。所以能做到:一份timeline资源,可以用于多个不同的地方

 

从ExposedReference解析绑定对象的方法

方法1

var bindingObj = _exposedRef.Resolve(playableGraph.GetResolver());

方法2:

var director = (PlayableDirector)playableGraph.GetResolver();
var obj = director.GetReferenceValue(_exposedRef.exposedName, out var isValid);
if (isValid && null != obj)
{
    var bindingObj = (Xxx)obj;
}

 

posted @ 2022-12-27 01:05  yanghui01  阅读(107)  评论(0编辑  收藏  举报