【Unity】Timeline探索記(4)第二個例子——動作特寫/子彈時間


寫在前面

  • 這次例子參考這篇實現博文(附帶項目下載),博文前面介紹非常具體,可惜后面特寫軌實現代碼不是按照我想要的標准四大件(data、mixer、clip、track)來組織的,所以這里我略過介紹,只記錄我在實現中遇到的問題。
  • 測試環境Unity2019.2.6:因為移動鏡頭用到了Cinemachine插件來實現,而該插件需要在Unity2018以上用Package安裝(以前Cinemachine是放在Asset Store里的,現在已下架),Unity2017也不支持最新的Cinemachine,所以這里統一用Unity2019.2.6來測試。
  • 因為實現重點是“動作特寫”,而不是研究Cinemachine,所以這里我沒有去深入了解Cinemachine。

 記錄

  • 用Cinemachine插件Create Virtual Camera后,MainCamera想要移動位置都要靠這個Virtual Camera/vcam,而且vcam要改變位置(位置+角度)只能在場景中手動調,不能直接改坐標。
  • 【問題1】在測試“動作特寫”時,注視target的vcam的中心很低(見下圖),而參考做的就很合適(見下下圖),怎么回事?

我做的測試:中心(黃線處)很低

參考:別人做的位置(黃線處)就很合適

  ——> targetGroup是不能移動的。但我的測試中放的target是人物腳下(見下方左圖),而參考放的target是人物的腰(見下方右圖),所以中心不一致。

                    

  • 【問題2】人物加完動作后,是原地做動作,不是朝着敵人跑過去,怎么朝敵人跑過去?——> 解決方案就是加入override軌,在override軌記錄人物的位置。
    • override軌如果mute的話,編輯時會看不到效果
    • 空白的override軌要先點錄制,隨便加入一個關鍵幀,后面雙擊override軌才能編輯效果
    • 我想過用MatchOffsets來改動作,但這功能是為了兩個動作順滑過渡用的,而人物要跑向敵人,這之間位移之大,是不能用MatchOffsets來完成的。
  • 【問題3】人物有rising和idle兩個動作融合不好,有位移 ——> 解決方法同上。
  • 參考博文用Animation Curve來實現時間變速,實現了自定義特寫軌,但它不按標准四大件data、mixer、clip、track來組織,只寫了一個腳本,而且還覆蓋了Unity暴露出的參考軌Playable Track。我給特寫軌起名為Time,按標准四大件重新組織了代碼,依次如下:
    • data:TimeBehaviour.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;


[Serializable]
public class TimeBehaviour : PlayableBehaviour
{
    public AnimationCurve curve;

}
    • mixer:TimeMixerBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;

public class TimeMixerBehaviour : PlayableBehaviour
{
    private float curTime = 0;
    private float maxTime = 0;

    public override void OnGraphStart(Playable playable)
    {

    }

    public override void OnGraphStop(Playable playable)
    {
        Time.timeScale = 1;
    }

    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {

    }

    public override void OnBehaviourPause(Playable playable, FrameData info)
    {

    }

    public override void PrepareFrame(Playable playable, FrameData info)
    {

    }

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        float scale = 1;
        int inputCount = playable.GetInputCount();
        for (int i = 0; i < inputCount; i++)
        {
            float inputWeight = playable.GetInputWeight(i);
            if (!Mathf.Approximately(inputWeight, 0f))
            {
                curTime += info.deltaTime;
                ScriptPlayable<TimeBehaviour> inputPlayable = (ScriptPlayable<TimeBehaviour>)playable.GetInput(i);
                TimeBehaviour input = inputPlayable.GetBehaviour();
                // maxTime 當前clip的時長
                maxTime = (float)PlayableExtensions.GetDuration(playable.GetInput(i));
                // curTime 當前clip的執行到哪個時刻
                scale = input.curve.Evaluate(curTime / maxTime);
            }
            else
            {
                curTime = 0;
            }
        }
        Time.timeScale = scale;

    }

}
    • clip:TimeClip.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[System.Serializable]
public class TimeClip : PlayableAsset, ITimelineClipAsset
{
    public TimeBehaviour template = new TimeBehaviour();

    public ClipCaps clipCaps
    {
        get
        {
            return ClipCaps.Blending; //選擇None,clip當然不會支持快捷鍵淡入淡出操作
        }
    }

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

    }

}
    • track:TimeTrack.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityEngine.UI;

[TrackColor(0.898f, 0.701f, 0.207f)]
[TrackClipType(typeof(TimeClip))]
[System.Serializable]
public class TimeTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        return ScriptPlayable<TimeMixerBehaviour>.Create(graph, inputCount);

    }

    public override void GatherProperties(PlayableDirector director, IPropertyCollector driver)
    {
        base.GatherProperties(director, driver);

    }
}
  • 和博文的參考代碼相比,我的代碼修改重點:
    • 【重點1】如下圖,博文這里的maxTime:因為不用mixer腳本,所以maxTime可以直接拿到當前clip的時長;

博文的maxTime

  我寫的mixer腳本如下圖,maxTime應該像這樣取值才行。如果像博文那樣寫,得到的maxTime會是無窮盡。

 我寫的maxTime

    • 【重點2】如下圖,博文這里的curTime會得到當前clip的進行時刻,意味着沒有clip或切到下一個clip時,curTime會變為0;

博文的curTime

  我寫的curTime如下圖,應該像這樣取值才會拿到當前clip的進行時刻。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM