美文网首页
Unity——#14 一段攻击

Unity——#14 一段攻击

作者: MisakiMel | 来源:发表于2019-07-15 22:11 被阅读0次

      这节打算新增一段攻击动画,但一般一段攻击是不够的,完整的一套平A应该要有三段或四段攻击,在以后我打算做三段攻击,这节先看看一段攻击如何实现。



      我认为现在这一层的动画状态机已经完整了,一些角色的基本动作例如走、跑、跳、翻滚都有了,不应该再往上添加动画了。所以我想把多段攻击放在一层新的状态机上,然后实现两者同时存在且互不干扰,说起来简单但做起来就不一定了。OK, Let's do it.
      首先就要新增一层Layer,在Animator的Layer里点选+号,新增一层Layer并命名为attack:


    attack
      新增之后第一件事并不是直接加动画哦,而是应该点开attack右边的小齿轮:

      里面的几个属性我来解释一下:
    • Weight 这个代表当前Layer的权重值,就是如果几个Layer同时存在的话,Animator会判断当前谁的Layer的权重值最大就去执行那个Layer的状态机,如果多个Layer的权重值都是最大(最大为1),那么都执行。第一层Layer(Base Layer)的权重值一直最大且不能修改,这是为了保持至少有一个Layer得到执行(我是这么认为的)。新增Layer的权重默认为0。
    • Mask 这里它接受一个Avatar Mask,何为Avatar Mask?Avatar Mask就是一个动画遮罩图层,通过选定人形模型身体上某些部位,这些部位会在某个Layer的动画执行时覆盖其他Layer的动画。这么说可能说得不太明白哦,我们可以新建一个Avatar Mask来看看其中的究竟。在Project视图点击右键create→Avatar Mask(我命名为fullbody):

       在它的Inspector视窗里能看到Humanoid选项,点开它:
      Avatar Mask
       能看到一个人形的大致关节图,可以通过点击这些关节,来选定哪些关节是你想在动画里面发生运动的,例如如果我只选中右手:
       那么使用这个Avatar Mask的Layer它里面的动画播放时将只有右手有动作(即使动画本身是整个身体都动起来),其余部分不为所动。
    • Blending 这个是Avatar Mask的混合方式,有additive和override两个选择,分别是附加和重载的意思,重载就是我上面说的那样(只有右手活动),而附加就是跟其他Layer一起播放。
    • sync 同步层,就是让该层复刻其他层的动画,其他层可以从Source Layer 选择。这个不多做解释,因为这节用不上。
    • IK Pass 这个是开启IK通道,让OnAnimatorIK()函数调用起来,如果不勾选这个选项即使你的代码里有OnAnimatorIK()这个方法,也不会被调用。而这个方法的作用是在运行时改变动画模型本身的一些参数。在日后讲述防御的时候会用到
        那么,由于现在还没有代码实现这个权重的变化,所以我们可以先把它拉满,主要是为了测试接下来添加的动画。另外,还要给它一个Avatar Mask,不过刚才在示范时我已经创建了一个命名为fullbody的Avatar Mask,所以可以直接套用给它,在给予了Avatar Mask后,该Layer的小齿轮左边会有一个M的符号:

        然后就可以加动画了,首先先要添加一个闲置状态的动画(idle),这是必须的,因为不可能这Layer一开始就是一段攻击,那么就一直攻击出不去了。然后在添加了一个一段攻击的动画,命名为attack1hA,意思是单手攻击(1h)的第一段攻击(A)。这两者先拉Transition,如图:

        先说简单的attack1hA→idle,这个没什么好说的,当前触发这个转换的条件就是当attack1hA播放完毕后自动转回idle。
        然后到idle→attack1hA,这个就有点难度了,首先考虑Has Exit Time,这个消勾,应该不用解释为什么了:

        其次来考虑触发这个转换的条件,肯定需要一个Trigger参数,像实现跳跃和翻滚那样,那么就给它整一个,命名为attack:

        然后在这个转换的Conditions里新增这个attack:

        这时就该考虑用代码把输入和信号连接起来了:
        在PlayerInput.cs里,宣告一个bool变量命名为attack,宣告一个string变量记录触发attack的keyname。
        public string keyC;
        public bool attack;
        void Upate(){
            ...
            attack = Input.GetKeyDown(keyC);
        }
    

      对于keyC我设置为j,对于该用什么按键攻击并没仔细考虑,现在先测试这个Trigger能不能正常使用要紧。



      然后在ActorController.cs里将这个bool变量的attack与Trigger attack联系起来:

        void Update(){
            ...
            if (pi.attack) {
                anim.SetTrigger ("attack");
            }
        }
    

      现在先来看看效果哦:


      噢上帝这效果简直不堪入目,一堆bug,我们来看看现在究竟都出些啥问题:
    1.完全没有了走路的动画,整个模型就在滑行
    2.按空格没有任何反馈,既不能跳又不能翻滚
    3.攻击随意转向
      遇到这么多问题哦肯定会慌张,感觉无从下手,都是像我这些小白常有的心态。先别急,回想一下是不是哪个环节出了问题,我沉思了一下,发现:

      可能就是这两个玩意搞得我一地鸡毛,重载嘛,全身嘛,然后在attack这个Layer的idle动画直接覆盖Base Layer的所有动画了,所以Base Layer的所有动画突然都不奏效了。那这肯定不是我想要的,怎么解决?我现在需要一个动态调节attack这个Layer的权重值的方法,只要权重值为0那么这两个玩意就不会奏效了,在我没有键入j键时,attack的权重值为0,此时就能正常播放Base Layer里的所有动画;在我键入j后,进入到attack的Layer里,此时将不能播放Base Layer里的动画,不过在攻击时不能跑跳滚也是合理的。这个该怎么实现呢?
      此时我们应该了解关于状态机的一个特点,这有助于我们去实现这个动态调节的方法。就是即使某层的的权重值为0,但它在状态机里依旧会运行,只是不会表现在角色的动画里,这个是我键入一次j的效果:

      那么我们就想,能不能这样做:当我们键入j,在进入到attack1hA的动画时,把attack这个Layer的权重值拉满,然后在attack1hA播放完毕,回到idle时,把权重值拉回0,感觉可行是不是?还能顺便把输入给锁死了。事不宜迟,马上动身!
      先在attack1hA和idle身上add Behaviour,就是FSMCallOnEnter,老招了,现在应该驾轻就熟了:


      至于怎么调节权重值,Animator给了一个方法就是Animator.SetLayerWeight(),它就只有一个Signature,那就是:
    public void SetLayerWeight(int layerIndex, float weight);
      参数不难理解:
    Parameters Mean
    layerIndex The layer index.
    weight The new layer weight.

      要注意的是,它接的是Layer的索引值,并不是Layer的名字,对于Animator的Layers,它是按索引值区分的,而这个索引值类似于数组那般,从0开始。



      像这样Base Layer的索引值就是0,attack的索引值就是1,以此类推。
      而这个函数的官方描述呢也是通俗易懂:

    Description
    Sets the weight of the layer at the given index.

      前面提到,Animator.SetLayerWeight()的第一个参数要求的是Layer的索引值,那么直接喂给它数字1是不好的,万一以后attack的位置改变了,这种写死的行为就会带来灾难。所以还是把这个写活好,用Layer的名字就最好,毕竟名字会改的情况不多,那么如何通过Layer的名字获得它当前的索引值呢,那就要用到Animator的另外一个函数Animator.GetLayerIndex(),这个函数也不难理解:
    public int GetLayerIndex(string layerName);给它layerName,就返还指定layerName的索引值。
      至此两个要用到的函数就绪,可以动手:

        void OnAttackIdleEnter()
        {
            pi.InputEnabled = true;
            anim.SetLayerWeight (anim.GetLayerIndex ("attack"), 0);
        }
    
        void OnAttack1hAEnter()
        {
            pi.InputEnabled = false;
            anim.SetLayerWeight (anim.GetLayerIndex ("attack"),1.0f);   
        }
    

      现在我想要做就实现了,可以看看效果:



      现在恢复正常了,可以走路了,而且平A时也不能转向了,但动作有点生硬,原因就是没给到它一个向前的冲量,而且现在attack这个Layer的权重值缺少了平滑处理,不是0就是1,导致两个Layer之间的切换太过突兀:



      可以看到这个蓝条,不是满就是空的,要对其进行平滑处理,做到慢慢拉满的效果。
      除了这些问题,其实还有一些很严重的问题,那就是现在我能进行跳劈加翻滚这种逆天操作,而且还有在奔跑时键入攻击键之后键入跳跃键,会在平A完接一个翻滚。这其实都是jump和attack两个Trigger的冲突问题,这么多问题,这节应该是讲不完了,只能留到下节在逐一击破。共勉!

    相关文章

      网友评论

          本文标题:Unity——#14 一段攻击

          本文链接:https://www.haomeiwen.com/subject/lmyokctx.html