半亩花草 发表于 2024-6-11 12:56:33

Unity2D游戏制作入门 | 12(之人物受伤和死亡的逻辑动画)

上期链接:Unity2D游戏制作入门 | 11(之人物属性及伤害盘算)-CSDN博客
上期我们聊到了人物的自身属性和受伤时的盘算,我们先给人物和野猪挂上属性和攻击属性的代码,然后通过触发器触发受伤的事件。物体(人物也好仇人也行)受伤时通常是在被攻击者的内部进行盘算的(这是有好处的,好比使用人物更复杂的血量盘算方式,我们在人物内部让它自己自动盘算就好了),我们使用关键词this来将Attacker内部的变量的访问权限赋给Character,然后我们又使用了计时器,让被攻击的物体在受到攻击后可以有短暂的无敌时间,计时器的写法你还知道吗?先创建三个变量一个布尔二个浮点,创建新的能赋给人物无敌的函数,然后判断人物如果不如那么人物的无敌状态就是True,然后赋给无敌时间(一个浮点变量我们是在Unity窗口手动赋给的,另一个浮点变量是内部进行使用它,在函数判断人物是否处在无敌时才用公共无敌时间赋给那个私有的浮点变量,如许就在内部激活了人物的无敌时间),我们在update中盘算人物无敌的倒计时,到点了就切换人物无敌状态为flase,然后在人物的Character中的TakeDamage函数中,我们判断人物如果不是无敌则进入扣血规则,然后如果人物的当前血量足够扣除一次血量则正常减血(如果人物血量为2仇人攻击为5,那么人物直接判断血量归零),不然人物死亡,而无敌状体的赋给与否全在人物扣血那部门功能中实行,如果人物血量归零那么将无法触发无敌状态。总的来说,上次就是多了两份通用的代码文件,分别是物体的属性和攻击(仅对需要这些的物体有效,如果是场景的死物一般不给属性如血量,也可以给看你设定,攻击应该是不要加上去的,除了陷阱啥的),虽然多了两份代码,我们照旧要弄清两份代码之间的访问和运行的逻辑,以及什么时候用它们。这期我们看看脚色的 受伤和死亡的逻辑和动画 。代码先下方:
public class PlayerAnimations : MonoBehaviour
{
    private Animator anim;//创建好这个组件变量后,如果不知道如何通关代码控制组件,可以去看代码手册
    private Rigidbody2D rb;//这个2D不能忽略,不然不报错但是人物跑不起来。
    private Player_control playercon;//人物控制
    private physicsCheck physicsCheck;

    private void Awake()
    {
      physicsCheck = GetComponent<physicsCheck>();
      anim = GetComponent<Animator>();
      rb= GetComponent<Rigidbody2D>();
      playercon = GetComponent<Player_control>();
    }

    private void Update()
    {
      SetAnimatons();//每帧时时检测,判断是否需要切换动画。
    }

    public void SetAnimatons()//需要做很多动画的切换,我们用这个函数来执行所有的动画切换。
    {
      anim.SetBool("isGround", physicsCheck.isGround);
      anim.SetFloat("velocityX",math.abs(rb.velocity.x));
      anim.SetFloat("velocityY",rb.velocity.y);
      anim.SetBool("shift", playercon.isShift);
      anim.SetBool("isDead", playercon.isDead);
    }

    public void PlayerHurt()
    {
      anim.SetTrigger("hurt");
    }

}
public class Player_control : MonoBehaviour
{
    public PlayerInputControl inputControl;
    public Vector2 inputDirection;//public变量表示一个公开的方法,它代表我在Unity窗口中可以查看得到。
    private Rigidbody2D rb;
    private physicsCheck physicsCheck;//创建物理检测变量

    private SpriteRenderer rbSprite;
   
    public float speed;
    public float walkSpeed;
    public float jumpForce;
    public float hurtForce;

   
    public bool isShift=false;

   
    public bool isHurt;
    public bool isDead;


    private void Awake()
    {
      physicsCheck = GetComponent<physicsCheck>();
      rb= GetComponent<Rigidbody2D>();
      inputControl = new PlayerInputControl();
      inputControl.GamePlayer.Jump.started += Jump;//按下空格那一瞬间执行事件Jump
      //inputControl.GamePlayer.Walk.performed += Walk;
      


      rbSprite = GetComponent<SpriteRenderer>();

    }

    private void OnEnable()
    {
      inputControl.Enable();
    }

    private void OnDisable()
    {
      inputControl.Disable();
    }

    private void Update()
    {
      inputDirection = inputControl.GamePlayer.Move.ReadValue<Vector2>();
      isShift= Keyboard.current.shiftKey.isPressed;//检测按键

    }

    private void FixedUpdate()//固定频率运行,即0.02秒执行一次。跟物理有关的放在这执行
    {
      if (!isHurt)
      {
            if (isShift && physicsCheck.isGround)
                Walk();
            else
                Move();
      }
      
    }

    public void Move()
    {
      rb.velocity = new Vector2(inputDirection.x*speed*Time.deltaTime,rb.velocity.y);


      //人物翻转
      if(inputDirection.x>0)
            rbSprite.flipX = false;
      if(inputDirection.x<0)
            rbSprite.flipX = true;
    }

    private void Jump(InputAction.CallbackContext obj)
    {
      if (physicsCheck.isGround)
      {
            rb.AddForce(jumpForce * transform.up, ForceMode2D.Impulse);//第一个参数表示给哪个方向施加力}
      }
    }

    private void Walk()
    {
      rb.velocity = new Vector2(inputDirection.x * walkSpeed * Time.deltaTime, rb.velocity.y);

      //人物翻转
      if (inputDirection.x > 0)
            rbSprite.flipX = false;
      if (inputDirection.x < 0)
            rbSprite.flipX = true;
    }

    public void GetHurt(Transform attcker)
    {
      isHurt = true;//受伤了
      //接下来执行反弹,不过前提是先把人物的速度停下来。
      rb.velocity = Vector2.zero;//表示物体的xy轴速度全设置为了。
      //接下来计算受伤的方向,然后ta要朝反方向弹射。
      Vector2 dir=new Vector2((transform.position.x-attcker.position.x),0).normalized;
      /*
         这里对上行代码进行说明,dir是direction方向的简写。计算方向我们就用人物坐标减去
      野猪或是其他的attacker的坐标对于x轴来说,如果人物在敌人左边那么它应该是负数,所以
      我们根据正负数作为它的方向乘上它的反冲力hurtForce(现实加速的效果)。dir算完后,我们
      需要不是数值的大小,而是方向值(我们按下键盘时dir有-1和1两种数值)。如果人物离野猪很远,那么x的值会非常大,乘反弹力会更大
      反而离得近数值会很小,所以我们要把数值归一化,即用到normalized的方法。
         
         */
      rb.AddForce(dir*hurtForce,ForceMode2D.Impulse);//添加瞬时的力反弹人物

    }

    public void PlayerDead()
    {
      isDead = true;
      inputControl.GamePlayer.Disable();
    }
}
public class Character : MonoBehaviour
{
   
    public float maxHp;//最大血量
    public float currentHp;//当前血量


   
    public float invincibleTime;//无敌时间
    private float invincibleCounter;//一个计数器,内部计算即可,不需要在窗口可以看得到
    public bool invincible;//为了能看见无敌,我们创建一个布尔值

    public UnityEvent<Transform> Ontakedamage;
    public UnityEvent Ondie;
    private void Start()//开始游戏时,要满血
    {
      currentHp = maxHp;
    }



    private void Update()
    {
      if(invincible)
      {
            invincibleCounter-=Time.deltaTime;
            if(invincibleCounter <= 0)
            {
                invincible = false;
            }
      }
    }


    //受到伤害
    public void TakeDamage(Attack attcker)
    {

      if (invincible)
            return;
      //Debug.Log(attcker.damage);

      if (currentHp - attcker.damage > 0)//血量健康才能减血
      {
      currentHp -= attcker.damage;//如果学过python,你会知道这行代码等于currentHp = currentHp - attcker.damage;
      TriggerInvincible();//触发无敌
            //执行受伤
      Ontakedamage?.Invoke(attcker.transform);

      }
      else
      {
            currentHp=0;
            //触发死亡
            Ondie?.Invoke();
      }

    }
   
    //受伤触发无敌
    private void TriggerInvincible()
    {
      if(!invincible)
      {
            invincible= true;
            invincibleCounter = invincibleTime;
      }
    }
}
public class HurtAninmation : StateMachineBehaviour
{
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
      animator.GetComponent<Player_control>().isHurt = false;
    }
}
正文:

玩家如果受到伤害,那么应该有对应的受伤动画,甚至血量归零时人物播放死亡的动画。找到我们之前的素材,我们可以先看看guid指导的动画内容,找到受伤的内容。我们先点击player,然后创建人物的动画文件(一定放在人物动画管理的文件下,如许方便找),然后创建人物的hurt动画,用右边的那四张图片(我们之前切割好的),然后Samples采样率我们调整到符合的数值(14的话感觉照旧有点快了所以我选了9)。
https://img-blog.csdnimg.cn/direct/23bea488a8f545da9099446bd8b42339.jpeg#pic_center
死亡动画编号为42-53。
https://img-blog.csdnimg.cn/direct/8e30bf8c55f64ed6a59a6dbea9236260.jpeg#pic_center
先将Animator中出现的我们刚刚创建的两个动画删去,我们注意到我们的人物在任何情况下都可能受伤,如我们跑步撞到野猪,在空中碰到仇人,这些都可能使人物受伤。如果人物受伤,应该凌驾在全部动画之上(我们先前制作的全部动画,如跳跃、闲置、跑步等),接下来我们创建新的方法。先不调用受伤动画,来看一个故意思的方法(UP话)。玩家如果进行受伤的状态, 那么玩家应该有闪烁的状态来表示玩家在这个阶段是受伤无敌的状态。我们在下图的这个地方创建新的动画的Layer图层,注意这些图层的名字下有一条线
https://img-blog.csdnimg.cn/direct/27c31665d79a435493ac1ffc382633ee.jpeg#pic_center
我们点击动绘图层右边的齿轮可以发现,有一个名为Weight权重的东西,这个权重会影响当前的这一层的动画的播放,你可以明白为:它的优先级是多少。下面的Blending为混合模式,Override是完全覆盖的意思。我们先将HurtLayer动绘图层的权重拉到1,然后混合模式切换成Additive(叠加)模式即当前层动画不会覆盖之前层(BaseLayer)的动画,在之前层的基础上为它进行添加。
https://img-blog.csdnimg.cn/direct/518ab7c68be6458982a86cfc8e14189d.jpeg#pic_center
我们在这个新地方创建新的空的状态,然后创建新的受伤动画即bule_hurt2。在这里我们不调用任何的图片,如今录制我们的动画片段。
https://img-blog.csdnimg.cn/direct/3a04be1843714624be88459795038f44.jpeg#pic_center
这个地方关键,我们去到人物的右边的组件Sprite Renderer,看到Color我们修改它RGB的通道,也可以修改它的阿尔法值,怎么说呢就是修改物体的透明程度(1的话就显示,0就隐身了),如果是改RGB中的值人物就会变色如变成赤色等。然后我们在动画管理下的Add Property,找到对应组件的功能,即我们找到Sprite Renderer下的控制color的选项即可。
https://img-blog.csdnimg.cn/direct/acb2fb47563541848023d529d7be3535.jpeg#pic_center
我们先在BaseLayer中删除我们创建的bule_hurt2动画,然后添加到HurtLayer中去。对了,hutr2一个六帧,采样率设置6即可,然后可以可以拖拽右边的6个点到1:0的位置,然后再0:2、 0:4 、1:0的位置分别设置阿尔法值为0.5、1、0.5。弄好后链接一根线到bule_hurt2,再创建一个新的参数为trigger范例的名称取hurt(你会注意到它右边是圆形的图案这是触发器)。在触发hurt2的条件中添加hurt参数这个条件(其他的条件如退出时间、转换时间都弄为无或是0,和之前的那些动画差不多就行),一旦hurt(左边红框的圆圈内)被勾选了即启动了,那么会自动播放这个受伤的动画。
https://img-blog.csdnimg.cn/direct/487c5c4c277043b08f17134d7aba2170.jpeg#pic_center
如果要由播放受伤的动画播放完了,我们需要回到之间的动画所以设置受伤播放完后回到初始的状态,这里返回的条件就设置为无,即什么都不加。
https://img-blog.csdnimg.cn/direct/d191e37e1f804a3b9176f7bb9d72d7de.jpeg#pic_center
我们进入到代码把它测试一下,我们打开代码PlayerAnimations。trigger的方法我们不能在update中实行,它是单次实行的函数,所以创建一个新的函数。
....
public void PlayerHurt()
    {
      anim.SetTrigger("hurt");
    }
那么我们时候实行调用PlayerHurt()呢?应该在Character代码中去实行:受伤我们需要播放人物的动画,以及人物被弹走不能再往前走(当然,我们受伤被弹走一般是不答应进行键盘操作的),甚至我们需要播放受伤的声音。这里又很关键了,试想一下,除了刚刚提到要被弹飞等事件,也可能还要调整一下我们ui的显示,就在受伤后有许多事情需要去实行。想让一次受伤去触发其他各种各样的代码或以后做UI都要来实行对应的方法,我们需要使用Unity的事件的方式。
public void TakeDamage(Attack attcker)
    {

      if (invincible)
            return;
      //Debug.Log(attcker.damage);

      if (currentHp - attcker.damage > 0)//血量健康才能减血
      {
      currentHp -= attcker.damage;//如果学过python,你会知道这行代码等于currentHp = currentHp - attcker.damage;
      TriggerInvincible();
      //执行受伤

      }
      else
      {
            currentHp=0;
            //触发死亡
      }
    }
接上面的内容,我们需要调用Unity的定名空间,接下来创建这些事件,我们先创建一个范例为UnityEvent的变量(如今要先弄受伤的事件),然后再public UnityEvent<>的尖括号中,我们可以传入一些参数,例如在受伤时盼望每次受伤都能按受伤的这个方向的反方向被击退,无论仇人照旧人物都应该是如许的。传递方向进去,其实也就是坐标,那么我们Transform范例的组件传进去。
using UnityEngine.Events;
public UnityEvent<Transform> Ontakedamage;
可能上面代码看得不是很懂,返回unity窗口就能直观地看到了(记得先生存写完的代码):Character组件就多了事情的内容了。这个东西是unity自带的,它的好处是以后我们学习UI时,点击的时候也会有这种事件的处置惩罚方式。在这个事件当中,我们可以点击加号来添加各种各样的函数方法,各种各样的功能。那么在一个事件触发时候,这里可以实行全部你添加进去的方法。如我们之前提到的人物受伤,如果触发了很可能我们要调用ui、播放音乐、实行方法,播放动画,全部的内容都可以放在这里。
https://img-blog.csdnimg.cn/direct/a36a930540a9461483557c40485f67d2.jpeg#pic_center
应用举例,我们把playerAnimations拖拽下去,我们找到代码中的playerHurt的函数方法,然后可以选择在何时运行它,如在游戏运行或是编辑器里时,这里我们选择只在游戏运行时实行。
https://img-blog.csdnimg.cn/direct/4815cab0137d4a8cb9177b9d17f6339b.jpeg#pic_center
接下来我们会到代码文件Character中,回看这行代码public UnityEvent<Transform> Ontakedamage;,**这个关于事件Ontakedamage,刚才的加号是代表我们把各种各样的方法注册到这个事件当中,接下来我们需要把这个事件启动起来,它其实有一个固定的写法。**注意新写入的代码Ontakedamage?.Invoke(attcker.transform);问号代表判断一下有没有任何方法添加进来,很可能之前的列表是空的,那我们就可能报错了,所以加上问号。**后面接点Invoke表示启动当前事件的内容(不要忘记打上括号)。**然后因为刚刚我们创建时有设置了参数,所以需要传一个transform进去,谁攻击我我就朝反方向移动,那么我们就要获得attcker它的transform了。如许,我们就实现了该函数(TakeDamage(Attack attcker))受伤数值减少、触发无敌,然后实行所以注册过来的受伤的函数方法。(目前我们只注册了受伤时播放闪烁的动画)
public void TakeDamage(Attack attcker)
    {

      if (invincible)
            return;
      //Debug.Log(attcker.damage);

      if (currentHp - attcker.damage > 0)//血量健康才能减血
      {
      currentHp -= attcker.damage;//如果学过python,你会知道这行代码等于currentHp = currentHp - attcker.damage;
         //触发无敌
      TriggerInvincible();
         //执行受伤
      Ontakedamage?.Invoke(attcker.transform);

      }
运行游戏,如果人物颠末野猪会有一个人物闪烁的结果,**我颠末测试发现:如果把野猪的Is trigger给关掉或胶囊触发器关闭不使用,那么人物颠末野猪,不管是顶着它走照旧穿过它都不能能触发受伤闪烁的结果。**如许我们就学习了怎样触发事件来实行一些方法。接下来我们给人物受伤切换回我们之前想要的受伤动画,然后按照我们之前给没有素材的动画添加受伤会变红闪烁的结果即可,然后如许我们就有了赤色受伤的动画,那么我们还需要让人物反弹回去。
https://img-blog.csdnimg.cn/direct/c37a95cbab3e4cc685dcd8b18a1a387f.jpeg#pic_center
我们受伤被击退的事件应该在人物控制的代码中被实行,因为控制人物的方向要被打断,如果不停向前跑但是受伤后需要被弹开。所以我们在player_control里面创建新的方法,来实行一个人物反弹的结果。我们需要设置一下玩家的状态,受伤就要制止我们其他的移动,也要反弹一段隔断。所以需要创建是否受伤的布尔值范例的变量isHurt。然后反弹的时候盼望有一个力把它弹开一段隔断。
        public bool isHurt;
        public float hurtForce;
....
    private void FixedUpdate()//固定频率运行,即0.02秒执行一次。跟物理有关的放在这执行
    {
      if (!isHurt)//没有进入受伤状态才能走动
      {
            if (isShift && physicsCheck.isGround)
                Walk();
            else
                Move();
      }
      
    }
.....
    public void GetHurt(Transform attcker)
    {
      isHurt = true;//受伤了
      //接下来执行反弹,不过前提是先把人物的速度停下来。
      rb.velocity = Vector2.zero;//表示物体的xy轴速度全设置为了。
      //接下来计算受伤的方向,然后ta要朝反方向弹射。
      Vector2 dir=new Vector2((transform.position.x-attcker.position.x),0).normalized;
      /*
      这里对上行代码进行说明,dir是direction方向的简写。计算方向我们就用人物坐标减去
      野猪或是其他的attacker的坐标对于x轴来说,如果人物在敌人左边那么它应该是负数,所以
      我们根据正负数作为它的方向乘上它的反冲力hurtForce(现实加速的效果)。dir算完后,我们
      需要不是数值的大小,而是方向值(我们按下键盘时dir有-1和1两种数值)。如果人物离野猪很远,那么x                  的值会非常大,乘反弹力会更大,反而离得近数值会很小,所以我们要把数值归一化,即用到normalized的                  方法。         
         */
      rb.AddForce(dir*hurtForce,ForceMode2D.Impulse);//添加瞬时的力反弹人物
    }
生存上面写好的代码会回到unity的窗口,我们需要把我们写好的人物受伤的函数方法添加到Ontakedamage这个事件中去,然后我们给人物被攻击的力改成8,我们发现人物被弹走后停不下来,原因是isHurt不停没用变回flase。
https://img-blog.csdnimg.cn/direct/dbadbf25b7204d0f985d8ea3ce82f74c.jpeg#pic_center
我们注意到animator中,我们看到受伤动画状态的右边,即我们可以添加一个代码,在这个动画实行的过程中,做一些代码的变化,我们先添加一个叫HurtAninmation的代码。输入完名称后,按下回车键,然后它会为你创建这个新的代码,不过代码被放在了我们project下的Assets下了,和一些文件目次同级了。
https://img-blog.csdnimg.cn/direct/f658f9ad82e244b09527eff5ca4493bb.jpeg#pic_center
https://img-blog.csdnimg.cn/direct/3a058094f6c845478a820622eb9ec99b.jpeg#pic_center
双击打开代码,我们可以看到这是一个unity自动为我们写好的模板,我们可以直接拿来用。它继承了**StateMachineBehaviour,也就是状态机的行为,**在做仇人我们也会单独来写。取消方法前面的解释就可以用了。第一方法是,当进去这个状态时实行,第二方法是当在实行这个动画的过程中我实行函数方法(如果这个状态也可称为动画时间比力久那么函数不停实行),第三个是当状态退出后,我们实行某某方法。So,当我们受伤播完并退出后,我们更改我们的人物isHurt的状态为flase即可。
https://img-blog.csdnimg.cn/direct/f2d46bb0760542128ad4e73a6cc9ab83.jpeg#pic_center
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
      animator.GetComponent<Player_control>().isHurt = false;
    }
注意我们用Ontakedamage事件的这个方法,我们即播放了动画,又可以播放脚本里的函数的逻辑。最后一个我们要做的就是人物的死亡,在任何状态下我们的人物都会死亡,所以我们到动画控制器中添加死亡的动画,然后HurtLayer的动绘图层我们把混合的模式改为Override覆盖。我们在Parameter中添加死亡参数进行判断isDead(布尔值)。然后给Any State连一根线到死亡动画中去,条件为isDead为true时,其他要么无要么为0。
https://img-blog.csdnimg.cn/direct/2dcc6ed35d8741a4bcbff2ce59ce3968.jpeg#pic_center
注意一下,我们的人物是否会不停进入死亡状态呢?不一定,如果我们重新开始游戏,那么人物要革新状态并退出死亡的动画(退出的条件各人应该都纯熟了,isDead为flse其他要么无要么为0)。
https://img-blog.csdnimg.cn/direct/f47b7cde14204924977b144b5eabed00.jpeg#pic_center
接下来把死亡动画链接到函数,我们需要在Character代码中添加死亡的事件方法。因为在人物死亡时,可能也要实行许多的事情,好比跳出UI的面板,除了关照你Game Over,还要关照你许多的信息,好比关照你的仇人不要继承攻击,人物不要再移动了。但是这次我们不需要传递任何参数,就是人物只是死亡。
//#Characte.cs
    public UnityEvent Ondie;//新加入,对应代码第22行
..........
public void TakeDamage(Attack attcker)
    {

      if (invincible)
            return;
      //Debug.Log(attcker.damage);

      if (currentHp - attcker.damage > 0)//血量健康才能减血
      {
      currentHp -= attcker.damage;//如果学过python,你会知道这行代码等于currentHp = currentHp - attcker.damage;
      TriggerInvincible();//触发无敌
            //执行受伤
      Ontakedamage?.Invoke(attcker.transform);

      }
      else
      {
            currentHp=0;
            //触发死亡
            Ondie?.Invoke();
      }
        }
那么人物的死亡是一个布尔值,所以它不是单次实行的,没有须要单独去写,我们直接把状态连接到Animation当中即可,并通过人物控制停止我们全部的人物操作如移动。
//#Player_control.cs
public bool isDead;
...
public void PlayerDead()
    {
      isDead = true;
      inputControl.GamePlayer.Disable();//我们之前输入系统的人物操作相关的内容。我们只关闭人物的操作,正常的ui操作还要保留的。
    }
然后在PlaterAnimation中把状态连接起来。
public void SetAnimatons()//需要做很多动画的切换,我们用这个函数来执行所有的动画切换。
    {
                   ........
      anim.SetBool("isDead", playercon.isDead);
    }
https://img-blog.csdnimg.cn/direct/e66a67c29c914f7785509f27a9e56df9.jpeg#pic_center
然后设置人物的血量为6进行死亡测试,不过你会发现人物会反复死亡,这是因为人物的死亡动画没有设置为只播放一次。把下图人物死亡的动画的循环关去即可,让它单次实行。一般来说许多动画都需要单次播放的,如果不连接到Any State中,它都不会有如许的问题,如果你链接了Any State且只盼望播放一次,一定要取消勾选Loop Time。
https://img-blog.csdnimg.cn/direct/f256ffdd1eba4f9892cef419caf2e938.jpeg#pic_center
总结:

​ 添加事件的方式特殊的方便,它能资助我们添加各种函数去实行一些事件,我们虽然只做了两个动画,即人物的受伤和死亡却操作了很久分析做游戏也不是很轻易的。这节可能没那么好明白,记得多多操作并明白这些步骤是为什么要这么做的,就连我自己都要反复看才气理出一点头绪。
​ 为了能记下Unity一些的特殊用法,照旧一些来稍微回味一下这期的内容吧。在创建人物受伤和死亡的动画之前,我们先是创建了人物被触发后可以或许闪烁的动画,如许的动画我们是通过组件Sprite Renderer里面的color功能区实现的。我们创建了一个新的动绘图层,修改它显示的权重并用于叠加到我们之前的动画上。触发受伤的动画是通过触发器来实现的,所以只需要单次实行,我们不会把它放入到Update函数中去,我们将在PlayerAnimations类中的函数PlayerHurt()放到Character代码中去实行,因为人物检测到血量减少后应该就实行人物受伤的动画,这很符合直觉。然后因为如果我们的人物受伤了,我们需要做许多的事情,如播放音乐、播放受伤动画等,所以我们使用了定名空间UnityEngine.Events,来给物体添加一些事件的功能,而且我们在Unity窗口的物体的组件上也能看到在对应的代码多出了添加一些注册事件的功能,然后我们需要在代码的对应位置启动某某事件固定格式为:事件名称.Invoke(参数)Ontakedamage?.Invoke(attcker.transform);。
​ 运行游戏后发现触发人物的受伤动画是正常的,接下来我们需要人物在进去受伤动画时,人物不应该能实行一些操作,如果移动跑步等,而且受伤被击退的事件应该在人物控制的代码中被实行,即如今我们需要确定的事件就是,人物受伤不能操作,且要被击退,而播放动画是在人物属性的代码中实行的,因为一旦扣血我们才播放受伤动画。在人物控制的代码文件中,我们创建了新的函数方法为GetHurt(Transform attcker),然后我们创建一个布尔值变量判断受伤,和一个受伤应该能弹飞人物的力的变量。然后该事件函数(人物被弹飞,且不能操作)的调用我们放入到刚刚Character的代码中去注册到事件Ontakedamege中去,这个地方就是如果在Character代码中如果事件Ontakedamage被启动了,相应的人物动画代码中的触发的参数hurt将被激活,然后实行人物受伤的动画,再接下来就是人物控制代码中的函数GetHurt(Transform attcker)被调用了,这是直接更新人物的受伤状态isHurt为true,那么,人物进入不能操作且被击飞的状态,这个连续的时间是是无限时间,因为没有设定何时把isHurt改为flase,在人物控制的FixedUpdate()函数中isHurt照旧true,达不到我们能操作的条件,所以我们在受伤动画退出的时间即受伤动画播放完后,我们通过在受伤的State中创建新的代码来在退出动画时更新我们人物受伤的状态,该代码文件的关键代码为:animator.GetComponent<Player_control>().isHurt = false;,即我们获取了人物控制的代码,在动画完全退出后我们把isHurt的状态改为flase,如许在我们人物被击飞后我们又获得了人物操作的控制权(是的,人物在受伤获得操控权后又飘了…)。然后就是注入人物死亡的动画,当人物的状态isDead为true时,我们让人物进入死亡而且全部操作都要停止,但是也只是限制人物的操作,像其他的如UI的操作我们没有停止,因为那是结算界面。我们在人物控制的代码文件中创建新的函数为PlayerDead(),人物死亡则isDead为true,而且关闭人物相关操作,并在人物动画中时时检测人物死亡因为重开游戏人物会满血达不到死亡的条件。
​ 大抵的流程就是如许了,不知道我有没有说清晰?唉这个部门就是比力混乱的,多看多想吧。
未尽事件以后可能会补充。
-------------------------结束线

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Unity2D游戏制作入门 | 12(之人物受伤和死亡的逻辑动画)