欢迎光临散文网 会员登陆 & 注册

用Timeline实现动画特写(下)

2019-12-12 17:59 作者:皮皮关做游戏  | 我要投稿

作者:Truly


大渣好。说书人放了无穷久的鸽子之后又回来了。

空中三百六十度回旋磕头以表歉意

书接上一回,上一篇介绍了Timeline的部分基础知识,并实现了主角的动画播放,本篇将接着把剩余的功能实现。本篇主要介绍实现的方法,具体的手感参数还需小伙伴们根据自己的动画慢慢调哦(体力活)。与格挡结合后效果图如下:


本篇实现功能:

1.Timeline控制角色位移

2.切换特写镜头

3.触发镜头抖动和播放粒子特效

4.子弹时间

5.动态绑定轨道对象(Binding)

本文不包含格挡与切割内容,有兴趣的小伙伴自行参考底部链接。



一、Timeline Signals

Unity 2019.1版本推出了Timeline Signals功能,具有“广播”特点,帮助发送事件。接下来将通过实现部分功能来熟悉Timeline Signals。

概念速览:

1.Signal Emitter(信号发射器)

信号发射器会放在Timeline上,它包含对信号资源的引用。如果当前播放时间点比发射器所在的时间点后,发射器会把信号资源发送到信号接收器。

(1)添加:对应Track右键 -> Add Signal Emitter


(2)Signal Emitter面板简介

  • Retroactive: 开始播放时的进度位置在发射器后也能回溯触发。

  • Emit Once:循环播放时只触发一次。

  • Create Signal:新建信号资源。

  • Add Signal Receiver:添加信号接收器。


2.Signal Asset(信号资源)

信号资源是发射器和接收器之间的联系。通常信号资源会用作标识符。

已有Signal信号资源时,新建或切换Signal如下图:


3.Signal Receiver(信号接收器)

当接收器知道信号已被触发时,它会激活关联到对应信号资源的反应。Signal Receiver会自动添加到绑定对象(轨道左侧)上。添加信号反应操作如下图:


大概逻辑:播放到信号发射器处 -> 发射信号 -> 对应信号的接收器激活反应

此外,还可以通过编写Marker脚本自定义标记,有兴趣的小伙伴自行了解。


二、设置被击退位移

上一篇结尾“组合”出敌人被击退的动画,现在开始通过Transform Tween TrackSignals设置击退的位移。(也可以通过点击小红点录制实现位移)

Ps:本文命名只是为了增加辨识度,无其他特殊含义。


1.添加Transform Tween Track

(1)添加Track:Timeline编辑器空白处右键 > Transform Tween Track,命名为ETransformTrack。(Inspector窗口可重命名)

(2)添加Clip:右键该Track > Add Transform Tween Clip,命名为BackTransClip。

(3)用Track Group(空白处右键)把主角和敌人的轨道分别装起来,便于整理。

(4)Transform Tween Clip部分属性简介:

  • Tween Positon:是否转换Position。

  • Tween Rotation:是否转换Rotation。

  • Twee Type(转换类型):Linear线性匀速、Deceleration减速、Harmonic(中间最快)、Custiom自定义。

  • Start Location:Clip起始的Transform。

  • End Location:Clip结束的Transform。

(5)给所有轨道左侧绑定框拖进对应的对象。


2.设置Transform Tween Track

在敌人被攻击的时候,通过给Start LocationEnd Location赋值来实现击退位移动。

(1)方法一(速览即可):脚本中通过层层遍历找到Location,然后为其赋值,有兴趣的小伙伴自行研究。 大概思路如下:


(2)方法二(本文粗暴做法):

①在主角里新建一个空的GameObject,命名为KickEndTrans,作为敌人被击退位移结束的Transform参考

②另外在Hierarchy再建一个空的GameObject,命名为TempKickEndTrans,拖进BackTransClip的End Location

③播放BackTransClip(Transform Tween Clip通过Signal调用方法,把TempKickEndTrans置到KickEndTrans的方位上,达到设置End Location的效果。(Start Location也如此设置)

代码如下:

    /// <summary>    /// 设置敌人被击退后的Transform    /// </summary>    public void OnSignal_SetKickEndTrans()

    {       

        //开始时敌人Transform        tempTargetTrans.position = cPlayer.targetTrans.position;// cPlayer.targetTrans为当前敌人Transform        tempTargetTrans.rotation = cPlayer.targetTrans.rotation;

        //结束时敌人Transform        tempKickEndTrans.rotation = kickEndTrans.rotation;

        tempKickEndTrans.position = kickEndTrans.position;

    }

 

④使用Signal调用方法:

  • 新建一个Signal Track,在开始击退的位置添加Signal Emitter,添加信号,命名为Kick。

  • Hierarchy中新建一个空对象,命名为SignalFunctions,拖进Signal Track左侧绑定框,在SignalFunctions的Inspector窗口可以看到自动添加的Signal Receiver。

  • 在Signal Receiver中选择对应信号,点击“+”添加反应,然后把方法所在对象拖进Reaction里,最后选择需要调用的方法。操作如图:

位移效果:


以上便是用Signal和Transform Tween Track 设置位移的粗暴做法,其他位移也可以如此设置,参数还需小伙伴们根据自己的动画具体情况具体分析。


三、镜头切换

镜头的切换主要是通过Cinemachine TrackCinemachine配合完成的。

(1)基本概念

Cinemachine核心组件包括Brain和Virtual Camera(虚拟相机):

  • Brain:负责相机的切换。

  • Virtual Camera(虚拟相机):负责拍摄。

Cinemachine提供了一些预设好行为方案,方便我们实现游戏中的功能。这次工程轨道中主要使用了Target Group Camera 和普通的Virtrual Camera,另外Timeline播放前人物用的是Free Look Camera(非本文主要介绍范围)。

  • Target Group Camera:可以在多个拍摄目标之间设置焦点,通过权重调节焦点偏移。

  • Virtrual Camera普通的相机。

  • Free Look Camera:允许玩家控制并围绕目标旋转的相机,可以用作第三人称相机。


(2)设置Target Group Camera

脚踢敌人过程中使用的是Target Group Camera ,以主角和敌人综合得出拍摄焦点。

①添加Target Group Camera后,Hierarchy窗口会出现一个CM vcam和一个对应的TargetGroup。关系如下图:


②调节TargetGroup

拍摄焦点通过目标、对应的权重以及半径综合得出。

  • Target:拍摄目标,本工程把玩家身上的摄像参考点拖进第一个参数框,通过脚本把敌人的Transform放进第二个参数框。

  • Weight:权重,用于调节焦点的偏移。

  • Radius:半径,用于调节相机距离。



在脚本中把敌人的Transform赋值给Target属性第二个参数框,代码如下(在播Timeline前调用),需要using Cinemachine;


   using Cinemachine;

   //...    /// <summary>    /// Timeline开始前设置Trans    /// </summary>    public void SetTransBeforeTL()

    {

        if (cPlayer.targetTrans)

        {

            //把敌人Trans赋值到Target Group组件里Target的第二个参数            targetGroup.m_Targets[1].target = cPlayer.targetTrans;  

            //...        }

    }

 

(3)设置Cinemachine Track

①添加Track:Timeline编辑窗口空白处 > 右键 > Cinemachine Track。

②把Main Camera拖进左侧绑定框。

③添加Clip:把所需的cinemachine拖进轨道,按需排序和混合即可,播放时会随着进度自动切换,重叠处会自动过渡。

本工程的切换顺序(仅供参考):Free Look Camera(起始过渡) > Target Group Camera (脚踢过程)> Virtrual Camera(跳劈时在头顶) > Free Look Camera


四、触发镜头抖动与播放粒子特效

1.屏幕抖动

(1)需要用到的组件:

①Cinemachine Impulse Source:发出抖动信号。

②Cinemachine Impulse Listener:收到信号,则镜头抖动。


(2)在Main Camera中添加Cinemachine Impulse Source组件:Inspector > Add Component > Cinemachine Impulse Source

部分属性:

  • Raw Signal:原始信号,可理解为抖动类型。使用Unity提供的信号类型:右侧齿轮 > Persets > 选择需要类型。

  • Amplitude Gain:振幅增益。

  • Frequency Gain:频率增益。

  • Impact Radius:影响半径。


(3)在需要抖动的Virtual Camera中添加Cinemachine Impulse Listener:

Inspector > Cinemachine 组件(底部)> Add Extension > CinemachineImpulseListener


2.粒子特效播放方法

在命中敌人的位置添加粒子特效,然后脚本中获取粒子特效,最后在方法中调用Play()播放。

    public ParticleSystem hitRed_Kick;

    /// <summary>    /// Kick特效    /// </summary>    public void OnSignal_SetKickEffect()

    {

        hitRed_Kick.Play();

    }

 

3.在攻击命中敌人的时候,通过Signal Receiver来触发镜头抖动和调用特效播放方法。

发出信号方法:CinemachineImpulseSource.GenerateImpulse();


五、子弹时间

本次是通过调节时间的缩放来实现子弹时间。

1.添加Time Dilation Track

2.添加Time Dilation Clip。部分属性:

  • Time Scale:时间缩放比例,1为正常时间,0为静止。

  • Blend Curves:混合曲线。


3.调节思路

(1)创建两个Time Dilation Clip:一个Time Scale为0,另一个Time Scale为1。(参数自行调节)

(2)把连个Clip重叠起来,通过调节Clip重叠长度和混合曲线,达到自己想要的效果。


六、动态绑定轨道对象

终结不同的敌人代表着动画的执行角色不同,因此需要在脚本中为轨道更换绑定的目标。

1.添加事件

(1)相关变量:

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

public class PAnimation : MonoBehaviour{

    CPlayer cPlayer;

    PSlicer pSlicer;                                        //切割脚本

    Animator animator;                                                

    Animator targetAnimator;

 

    RuntimeAnimatorController player_AC;                    //记录玩家AnimatorController    RuntimeAnimatorController target_AC;                    //记录目标AnimatorController

    public PlayableDirector playableDirector;               //TimelineDirector;    public TimelineAsset tL_StrikeBack;                     //反击的TimelineAsset

    //信号相关    SignalFunctions signalFunctions;                        //信号方法脚本    Dictionary<string, PlayableBinding> bindingDict;        //存储    //...}

 

(2)事件

①PlayableDirector.played:在PlayableDirector 开始播放时调用。

②PlayableDirector.stopped:在PlayableDirector停止播放时调用。

    void Start()

    {

       // ...        //添加事件        playableDirector.played += OnPlayableDirectorStart;     //开播时执行        playableDirector.stopped += OnPlayableDirectorStopped;  //停播时执行    }

 

2.释放技能时:

(1)指定播放片段(TimelineAsset)。

    public void PlayTimeline()

    {

        //指定TimelineAsset(播放片段)        playableDirector.playableAsset = tL_StrikeBack; 

        //储存Binding        SaveBindings();

        playableDirector.Play();

    }

 

(2)用字典储存每个轨道:PlayableBinding.streamName 获取轨道名字,作为字典的Key。

代码如下(需要using UnityEngine.Playables;和using UnityEngine.Timeline;):

using UnityEngine.Playables;using UnityEngine.Timeline;//... //储存绑定的Track左侧,换新TimelineAsset时调用    public void SaveBindings()

    {

        foreach (PlayableBinding binding in playableDirector.playableAsset.outputs)

        {

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

            {

                bindingDict.Add(binding.streamName, binding);

            }

        }      

    }

 



3.Timeline播放前

(1)置空AnimatorController避免播放TimeLine过程中执行其他状态。(也可在动画状态机中添加表示正在播放Timeline的状态来解决)

(2)通过检索字典给轨道绑定对象:

  • 绑定轨道左侧对象:PlayableDirector.GetGenericBinding(Object key, Object value);

  • Key:可用PlayableBinding.sourceObject获取。

  • Value:需要绑定的对象。

代码如下(含注释):


/// <summary>    /// 播放前执行    /// </summary>    /// <param name="aDirector"></param>    void OnPlayableDirectorStart(PlayableDirector aDirector)

    {

        cPlayer.characterController.enabled = false;    //关闭CharacterController,不然有冲突           animator.applyRootMotion = true;                //应用RootMotion      

        //暂存AnimatorController        player_AC = animator.runtimeAnimatorController;

        //把AnimatorController暂时置空,避免播Timeline时动画状态机还在执行其他状态。        animator.runtimeAnimatorController = null;

 

        //敌人AnimatorController暂时置空,播完置回去。        targetAnimator = cPlayer.targetTrans.GetComponent<Animator>();

        target_AC = targetAnimator.runtimeAnimatorController;

        targetAnimator.runtimeAnimatorController = null;      

 

        //把玩家Animator绑进轨道        playableDirector.SetGenericBinding(bindingDict["PlayerAniTrack"].sourceObject, animator);

        playableDirector.SetGenericBinding(bindingDict["PTransformTrack"].sourceObject, animator.transform);

        //把敌人Animator绑进轨道        playableDirector.SetGenericBinding(bindingDict["EnemyAniTrack"].sourceObject, cPlayer.targetTrans.GetComponent<Animator>());

        playableDirector.SetGenericBinding(bindingDict["ETransformTrack"].sourceObject, cPlayer.targetTrans);

        //Clip 参考Trans设置        signalFunctions.SetTransBeforeTL();

    }


 

4.动画播放完后,用PlayableDirector.Evaluate()估算Timeline中各对象,否则会回到开播的位置,然后把播放的TimelineAsset置为null。代码如下(含注释):

/// <summary>    /// 播放完执行    /// </summary>    /// <param name="aDirector"></param>    void OnPlayableDirectorStopped(PlayableDirector aDirector)

    {

        playableDirector.Evaluate();            //评估对象所在位置,否则回到播放前位置        playableDirector.playableAsset = null;  //playableDirector里的TimlineAsset置Null

        //重置玩家播放Timeline前的状态        cPlayer.characterController.enabled = true;

        cPlayer.isStrikingBack = false;

        animator.applyRootMotion = false;

        animator.runtimeAnimatorController = player_AC;

       

        //重置敌人播放Timeline前的状态        if (targetAnimator)

        {

            targetAnimator.runtimeAnimatorController = target_AC;

            //...        }

    }

 

5.踩过的坑

(1)角色身上CharacterController在激活状态,播放Timeline时角色会不能位移。

(2)结束播放时,没有执行PlayableDirector.Evaluate(),角色回到开始播放位置。

(3)播放结束,没有把PlayableDirector.playableAsset置空,非Apply Root Motion时角色不能移动。


结语:本文结合了Timeline Signals和各种Track实现了一系列的功能,虽然已花了较长时间查询资料,但是仍存在很多不完善的地方,时间关系只好先介绍到这里(自己开的坑,头发掉满地也只能含泪勉强填完)。希望对大家初步了解Timeline有所启发,如有问题欢迎讨论。


相关资料

1.Timeline Signals:https://connect.unity.com/p/shi-yong-unity-2019-1zhong-de-timeline-signals

2.Timeline动态赋值:https://zhuanlan.zhihu.com/p/29585350

3.Ezy-Slice切割教程:https://www.bilibili.com/video/av65373505

4.从SkinnedMeshRenderer获取正确显示的普通Mesh:https://blog.csdn.net/Traip/article/details/88095446

5.格挡:https://zhuanlan.zhihu.com/p/83607025

链接:https://pan.baidu.com/share/init?surl=K1RX_qmmPfcjQazJHpNBJA
提取码:stll


咱们的游戏开发交流群也欢迎强势插入:869551769

希望参与线下游戏开发学习的,欢~~~~~~迎访问:http://www.levelpp.com/


用Timeline实现动画特写(下)的评论 (共 条)

分享到微博请遵守国家法律