最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【Unity】TimeLine&Cinemachine系列教程——動態(tài)賦值,我要打十個!

2017-10-08 17:45 作者:皮皮關做游戲  | 我要投稿

前言

TimeLine在上一篇介紹了TimeLine的各個功能特性,已經(jīng)用TimeLine做了一個固定的特寫鏡頭。但是要用好TimeLine還得去學會如何去掌控它,因此今天主要內(nèi)容之一是用代碼賦值修改TimeLine中軌道的參數(shù)(為了能更好的做出效果,幾乎快做了一個格斗游戲了,想做動作游戲想的熱血沸騰~,最后想著還要出教程,還是停下來老老實實做功能)。

前前后后遇到了很多坑點,TimeLine有許多沒法力所能及的地方,在這篇文章結(jié)尾會將主要的問題寫出來,避免大家繼續(xù)踩坑。


最終效果

Track賦值

創(chuàng)建了TimeLine的物體會有一個綁定列表

綁定列表對應我們做TimeLine各個控制軌道

我們是可以對軌道修改名字的,這個修改后的名字就是Bindings的key值。

腳本中可以獲取到PlayableDirector類,通過SetGenericBinding函數(shù)賦值物體。

       var mDirector = new PlayableDirector;

       mDirector.SetGenericBinding("軌道名字","軌道綁定的物體");

這一步是比較簡單的,完成這一步后,我們可以創(chuàng)建兩個動畫,在游戲中觸發(fā)TimeLine后,動態(tài)給軌道賦值。比如給攻擊動畫軌道賦值玩家模型,給受擊動畫軌道賦值怪物模型。


/*有的讀者可能會問,我們?nèi)绻龉艉捅粨粜Ч脑捒梢灾苯影褎幼鞣旁诮巧刂破骼锩妫瑳]必要用TimeLine實現(xiàn)。的確是這樣,但是如果游戲中模型動作資源太多,比如戰(zhàn)神這款游戲,每一個精英怪都會有一個動作特寫,如果一開始全部放在動畫控制器中,會造成動畫十分混亂。而使用TimeLine,相當于我們動態(tài)給模型加載動畫,當使用到這個動畫的時候再加載它,這樣就會讓動畫控制器非常的干凈簡潔,也節(jié)約了動畫占用的內(nèi)存*/


TimeLineClip賦值

接下來我們會動態(tài)給軌道內(nèi)的Clip賦值,比如說Cinemachine的攝像機,和攝像機觀察的物體、跟隨的物體。

獲取到Clip的代碼我是做了一個字典來在playableAsset.outputs輸出信息中儲存了所有軌道信息。

    public void Init()

    {

        ……

        ……

        foreach (var at in mDirector.playableAsset.outputs)

        {

            if (!bindingDict.ContainsKey(at.streamName))

            {

                bindingDict.Add(at.streamName, at);

            }

        }


        var CinemachineTrack = bindingDict["Cinemachine"].sourceObject as Cinemachine.Timeline.CinemachineTrack;


        foreach (var clip in CinemachineTrack.GetClips())

        {

            //TODO

        }

    }

而沒有用GetGenericBinding函數(shù)是因為傳入的值是Object類型,目前沒找到一個有效傳入數(shù)據(jù)。


給Clip賦值有幾個重要的地方,一是Clip的名字是TimeLineClip的displayName,TimeLineClip.asset是片段資源,二是賦值的時候得創(chuàng)建ExposedReference類型的結(jié)構體,不然直接賦值是沒法生效的。

    public void Init()

    {

        ……

        ……


        foreach (var info in CinemachineTrack.GetClips())

        {

            if (info.displayName == "CinemachineShot")

            {

                var cameraInfo = info.asset as Cinemachine.Timeline.CinemachineShot;

                var vcam1 = GameObject.Find("CM vcam1").GetComponent<CinemachineVirtualCameraBase>();

                var setCam = new ExposedReference<CinemachineVirtualCameraBase>();

                setCam.defaultValue = vcam1;

                cameraInfo.VirtualCamera = setCam;

            }

        }

    }

Clip如果賦值完畢后,要修改Clip的參數(shù),必須解析playableGraph,不然修改的目標并不是實際游戲中的物體。

    public void Init()

    {

        ……

        ……


        var cameraInfo = info.asset as Cinemachine.Timeline.CinemachineShot;

        var vcam2 = cameraInfo.VirtualCamera.Resolve(mDirector.playableGraph.GetResolver());

        vcam2.LookAt = mControl.transform.Find("Tran_Chest").transform;

        vcam2.Follow = mControl.transform;

    }

這里有坑點:playableGraph是TimeLine運行時才會創(chuàng)建生存,否則為空。因此我們修改的這些值實際是 TimeLine已經(jīng)播放的時候修改的。如果TimeLine在OnGraphStart函數(shù)中用了的值,我們這里修改后是無效的。


編寫觸發(fā)腳本

做這個之前有一個小插曲,本來我是想用TimeLine的自定義腳本軌道來搞定被抓取后怪物的位置修正的,結(jié)果發(fā)現(xiàn)不能用TimeLine來做,因為TimeLine的賦值修改會直接對所有用到這個TimeLine資源的物體一起賦值(這個bug目前沒有找到解決方法),所以用自定義腳本軌道上處理位置修正,就會保留當前的目標信息,下次抓另一個怪物的時候就會兩個物體一起運動(神奇的bug)。

所以我直接把抓取的時候位置修正代碼和觸發(fā)TimeLine的代碼一起了,代碼如下:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.Events;

using UnityEngine.Timeline;

using UnityEngine.Playables;

using Cinemachine.Timeline;

using Cinemachine;


public class KillControl : MonoBehaviour

{

   public GameObject mKillInfo;

   [HideInInspector]

   public PlayableDirector mDirector;

   Dictionary<string, PlayableBinding> bindingDict = new Dictionary<string, PlayableBinding>();

   public CharaControl playerControl;

   private CharaControl mControl;

   public Vector3 offsetPos;

   public Vector3 offsetRot;

   public UnityAction OnFinishEvent;



   public void Start()

   {

       mControl = GetComponent<CharaControl>();


       mKillInfo = Instantiate(mKillInfo);


       mKillInfo.transform.SetParent(mControl.transform);


       mDirector = mKillInfo.GetComponent<PlayableDirector>();


       foreach (var at in mDirector.playableAsset.outputs)

       {

           if (!bindingDict.ContainsKey(at.streamName))

           {

               bindingDict.Add(at.streamName, at);

           }

       }

   }


   public void Play()

   {

       mControl.transform.position = playerControl.transform.position + playerControl.transform.rotation * offsetPos;

       mControl.transform.rotation = Quaternion.LookRotation(playerControl.transform.position - mControl.transform.position);

       mControl.transform.localEulerAngles += offsetRot;



       mKillInfo.gameObject.SetActive(true);

       mDirector.Play();


       mDirector.SetGenericBinding(bindingDict["Player"].sourceObject, playerControl.mAim);

       mDirector.SetGenericBinding(bindingDict["Enemy"].sourceObject, mControl.mAim);

       mDirector.SetGenericBinding(bindingDict["Cinemachine"].sourceObject, Camera.main.GetComponent<Cinemachine.CinemachineBrain>());

       var CinemachineTrack = bindingDict["Cinemachine"].sourceObject as Cinemachine.Timeline.CinemachineTrack;


       foreach (var info in CinemachineTrack.GetClips())

       {

           if (info.displayName == "CinemachineShot")

           {

               var cameraInfo = info.asset as Cinemachine.Timeline.CinemachineShot;

               var vcam1 = GameObject.Find("CM vcam1").GetComponent<CinemachineVirtualCameraBase>();

               var setCam = new ExposedReference<CinemachineVirtualCameraBase>();

               setCam.defaultValue = vcam1;

               cameraInfo.VirtualCamera = setCam;

           }


           if (info.displayName == "CM vcam2")

           {

               var cameraInfo = info.asset as Cinemachine.Timeline.CinemachineShot;

               var vcam2 = cameraInfo.VirtualCamera.Resolve(mDirector.playableGraph.GetResolver());

               vcam2.LookAt = mControl.transform.Find("Tran_Chest").transform;

               vcam2.Follow = mControl.transform;

           }

       }

   }


   private void Update()

   {

       if (mDirector.gameObject.activeInHierarchy)

       {

           if (mDirector.state == PlayState.Paused)

           {

               mDirector.gameObject.SetActive(false);

               if (OnFinishEvent != null)

               {

                   OnFinishEvent();

               }

           }

       }

   }

}

腳本功能很簡單,Start里面創(chuàng)建好資源,并做了一個索引字典,Play的時候動態(tài)賦值。唯一要注意的是目前沒找到TimeLine的結(jié)束回調(diào)事件,因此只能用判斷運行狀態(tài)來處理結(jié)束邏輯。為了精確時間,我將判斷函數(shù)放在了Update中。

將這個腳本掛載到怪物身上,設置好綁定的TimeLine和修正的位置、旋轉(zhuǎn)信息。


接下來只要我們特殊攻擊打倒了怪物,我們就直接播放TimeLine:

這里有必要說一下,在編輯狀態(tài)里面,有時候會發(fā)現(xiàn)鏡頭里面動畫位置信息效果不對。這是正常的,因為我們沒有去Key位置,TimeLine沒有記錄位置信息的時候,編輯模式位置就會是一個位置點和方向。

我們可以修改動畫的偏移,這樣就可以在編輯模式下看到正常效果了

攻擊判定

對于角色如何控制的由于是測試代碼,想做了解的同學可以去下載工程源碼。這里對如何觸發(fā)攻擊做一個簡單講解。

如下,角色攻擊的骨骼會綁定攻擊盒子(觸發(fā)器),當攻擊動畫進入判定攻擊時間的時候?qū)⒑凶蛹せ?,在收招的時候盒子判定會關閉。在此之間如果檢測到了怪物的受擊盒子(怪物會有一個受擊判定盒子),就提前關閉攻擊觸發(fā)器,進入TimeLine播放階段。

附上主要觸發(fā)代碼,根據(jù)不同的index索引ID判斷身體哪個部位擊中了怪物。攻擊和被擊我都是用一個代碼來做判定的,這樣方便我們直接可以得到我們打的是誰的被攻擊部位。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.Events;


public class HitTrigger : MonoBehaviour

{

   /// <summary>

   /// 觸發(fā)器

   /// </summary>

   public Collider mCol;

   /// <summary>

   /// 控制該觸發(fā)器的角色

   /// </summary>

   public CharaControl mParent;

   /// <summary>

   /// 碰撞器Id

   /// </summary>

   public string index;

   /// <summary>

   /// 是否激活

   /// </summary>

   public bool isActive;

   /// <summary>

   /// 當前碰撞到的物體列表

   /// </summary>

   public List<GameObject> colList = new List<GameObject>();

   /// <summary>

   /// 碰撞事件

   /// </summary>

   public UnityAction<HitTrigger> TriggerFuncEvent;



   public void Start()

   {

       mCol = gameObject.GetComponent<Collider>();

       if (mCol.isTrigger)

       {

           mCol.enabled = false;

       }

   }


   public void OnTriggerEnter(Collider other)

   {

       SetHitTrigger(other);

   }


   public void OnTriggerStay(Collider other)

   {

       SetHitTrigger(other);

   }



   public void SetHitTrigger(Collider other)

   {

       if (!isActive)

       {

           return;

       }


       var hitObj = other.gameObject;


       if (!colList.Contains(hitObj))

       {

          var triggerScr=   hitObj.GetComponent<HitTrigger>();


           if (triggerScr==null||triggerScr.mParent == mParent)

           {

               return;

           }


           colList.Add(hitObj);


           if (TriggerFuncEvent != null)

           {

               TriggerFuncEvent(triggerScr);

           }

       }

   }




   public void SetColActive(bool rActive)

   {

       isActive = rActive;

       if (mCol.isTrigger)

       {

           mCol.enabled = rActive;

       }


       if (!isActive)

       {

           colList.Clear();

       }

   }

}


TimeLine做動畫控制的問題

Bake Into Pose選項作用

再次提及一個Humanoid動畫設置,之前有點模糊的地方,Bake Into Pose如果勾選,這個動畫將會只將動畫信息放在動畫中,不會修改根節(jié)點位置。而取消勾選的話,將會將動畫位移信息保存到根骨骼中。

勾與不勾效果對比:

勾選:TimeLine播放完后動畫轉(zhuǎn)身

取消勾選:播放完畢后,位置信息保存到了根節(jié)點上。

TimeLine無法很好處理兩個根節(jié)點動畫融合

實際上再非編輯模式下,是正常的,感覺是在編輯模式下,Animtor的App Root Motion選項功能沒有激活。如果有這個問題,可以用Override動畫修改位置,或者使用動畫偏移修改位置

TimeLineClip名字自動修改

比如我將未賦值的相機軌道Clip賦值為01

當拖到層級窗口時,會自動修改名字成CinemachineShot

這讓動態(tài)修改Clip的難度直線上線,目前沒有找到很好的辦法,只能賦值一個不用的初始物體解決。


總結(jié)

TimeLine是Unity的一個很好的功能,但是功能不完美,還是存在許多坑的地方。比如賦值Clips會需要PlayableGraph類,比如自定義腳本賦值問題,動畫編輯模式下無法很好查看根節(jié)點動畫問題。但是總的來說避免了坑點后加功能的時候是非常方便的。

有興趣的同學可以下載工程源碼看看:

https://github.com/chs71371/TimeLineBattle






對游戲開發(fā)感興趣的同學,歡迎圍觀我們:【皮皮關游戲開發(fā)教育】 ,會定期更新各種教程干貨,更有別具一格的線下小班教育。在你學習進步的路上,有皮皮關陪你!~

我們的官網(wǎng)地址:http://levelpp.com/

我們的游戲開發(fā)技術交流群:610475807

我們的微信公眾號:皮皮關


【Unity】TimeLine&Cinemachine系列教程——動態(tài)賦值,我要打十個!的評論 (共 條)

分享到微博請遵守國家法律
桐梓县| 新平| 台东县| 无锡市| 怀柔区| 合水县| 石门县| 赤峰市| 凤山县| 屏东县| 航空| 朔州市| 宜都市| 沙雅县| 玛纳斯县| 荥阳市| 囊谦县| 邳州市| 宜兴市| 邹平县| 远安县| 莱西市| 乐陵市| 宁国市| 宜章县| 赤壁市| 新密市| 林口县| 赤城县| 博客| 堆龙德庆县| 依兰县| 吴堡县| 靖西县| 响水县| 明水县| 大名县| 大渡口区| 和顺县| 邹城市| 凉城县|