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;
    [Header("基本参数")]
    public float speed;
    public float walkSpeed;
    public float jumpForce;
    public float hurtForce;

    [Header("按键按下")]
    public bool isShift=false;

    [Header("状态")]
    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
{
    [Header("基本属性")]
    public float maxHp;//最大血量
    public float currentHp;//当前血量


    [Header("受伤无敌")]
    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)。
在这里插入图片描述

死亡动画编号为42-53。
在这里插入图片描述

先将Animator中出现的我们刚刚创建的两个动画删去,我们注意到我们的人物在任何情况下都可能受伤,如我们跑步撞到野猪,在空中碰到敌人,这些都可能使人物受伤。如果人物受伤,应该凌驾在所有动画之上(我们先前制作的所有动画,如跳跃、闲置、跑步等),接下来我们创建新的方法。先不调用受伤动画,来看一个有意思的方法(UP话)。玩家如果进行受伤的状态, 那么玩家应该有闪烁的状态来表示玩家在这个阶段是受伤无敌的状态。我们在下图的这个地方创建新的动画的Layer图层注意这些图层的名字下有一条线
在这里插入图片描述

我们点击动画图层右边的齿轮可以发现,有一个名为Weight权重的东西,这个权重会影响当前的这一层的动画的播放,你可以理解为:它的优先级是多少。下面的Blending为混合模式,Override是完全覆盖的意思。我们先将HurtLayer动画图层的权重拉到1,然后混合模式切换成Additive(叠加)模式即当前层动画不会覆盖之前层(BaseLayer)的动画,在之前层的基础上为它进行添加。

在这里插入图片描述

我们在这个新地方创建新的空的状态,然后创建新的受伤动画即bule_hurt2。在这里我们不调用任何的图片,现在录制我们的动画片段。

在这里插入图片描述

这个地方关键,我们去到人物的右边的组件Sprite Renderer,看到Color我们修改它RGB的通道,也可以修改它的阿尔法值,怎么说呢就是修改物体的透明程度(1的话就显示,0就隐身了),如果是改RGB中的值人物就会变色如变成红色等。然后我们在动画管理下的Add Property,找到对应组件的功能,即我们找到Sprite Renderer下的控制color的选项即可。
在这里插入图片描述

我们先在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(左边红框的圆圈内)被勾选了即启动了,那么会自动播放这个受伤的动画。
在这里插入图片描述

如果要由播放受伤的动画播放完了,我们需要回到之间的动画所以设置受伤播放完后回到初始的状态,这里返回的条件就设置为无,即什么都不加。

在这里插入图片描述

我们进入到代码把它测试一下,我们打开代码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、播放音乐、执行方法,播放动画,所有的内容都可以放在这里。
在这里插入图片描述

应用举例,我们把playerAnimations拖拽下去,我们找到代码中的playerHurt的函数方法,然后可以选择在何时运行它,如在游戏运行或是编辑器里时,这里我们选择只在游戏运行时执行。

在这里插入图片描述

接下来我们会到代码文件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给关掉或胶囊触发器关闭不使用,那么人物经过野猪,不管是顶着它走还是穿过它都不能能触发受伤闪烁的效果。**这样我们就学习了如何触发事件来执行一些方法。接下来我们给人物受伤切换回我们之前想要的受伤动画,然后按照我们之前给没有素材的动画添加受伤会变红闪烁的效果即可,然后这样我们就有了红色受伤的动画,那么我们还需要让人物反弹回去。
在这里插入图片描述

我们受伤被击退的事件应该在人物控制的代码中被执行,因为控制人物的方向要被打断,如果一直向前跑但是受伤后需要被弹开。所以我们在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。
在这里插入图片描述

我们留意到animator中,我们看到受伤动画状态的右边,即我们可以添加一个代码,在这个动画执行的过程中,做一些代码的变化,我们先添加一个叫HurtAninmation的代码。输入完名称后,按下回车键,然后它会为你创建这个新的代码,不过代码被放在了我们project下的Assets下了,和一些文件目录同级了。

在这里插入图片描述
在这里插入图片描述

双击打开代码,我们可以看到这是一个unity自动为我们写好的模板,我们可以直接拿来用。它继承了**StateMachineBehaviour,也就是状态机的行为,**在做敌人我们也会单独来写。取消方法前面的注释就可以用了。第一方法是,当进去这个状态时执行,第二方法是当在执行这个动画的过程中我执行函数方法(如果这个状态也可称为动画时间比较久那么函数一直执行),第三个是当状态退出后,我们执行某某方法。So,当我们受伤播完并退出后,我们更改我们的人物isHurt的状态为flase即可。
在这里插入图片描述

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。

在这里插入图片描述

注意一下,我们的人物是否会一直进入死亡状态呢?不一定,如果我们重新开始游戏,那么人物要刷新状态并退出死亡的动画(退出的条件大家应该都熟练了,isDead为flse其他要么无要么为0)。
在这里插入图片描述

接下来把死亡动画链接到函数,我们需要在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);
    }

在这里插入图片描述

然后设置人物的血量为6进行死亡测试,不过你会发现人物会反复死亡,这是因为人物的死亡动画没有设置为只播放一次。把下图人物死亡的动画的循环关去即可,让它单次执行。一般来说很多动画都需要单次播放的,如果不连接到Any State中,它都不会有这样的问题,如果你链接了Any State且只希望播放一次,一定要取消勾选Loop Time。
在这里插入图片描述

总结:

​ 添加事件的方式特别的方便,它能帮助我们添加各种函数去执行一些事件,我们虽然只做了两个动画,即人物的受伤和死亡却操作了很久说明做游戏也不是很容易的。这节可能没那么好理解,记得多多操作并理解这些步骤是为什么要这么做的,就连我自己都要反复看才能理出一点头绪。

​ 为了能记下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,而且关闭人物相关操作,并在人物动画中时时检测人物死亡因为重开游戏人物会满血达不到死亡的条件。

​ 大致的流程就是这样了,不知道我有没有说清楚?唉这个部分就是比较混乱的,多看多想吧。

未尽事宜以后可能会补充。

-------------------------结束线

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-09 16:48:02       142 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-09 16:48:02       156 阅读
  3. 在Django里面运行非项目文件

    2024-06-09 16:48:02       131 阅读
  4. Python语言-面向对象

    2024-06-09 16:48:02       141 阅读

热门阅读

  1. 移动端前端开发遇到过的Andorid和IOS的差异记录

    2024-06-09 16:48:02       32 阅读
  2. Audio音频资源播放

    2024-06-09 16:48:02       50 阅读
  3. springboot + easyRules 搭建规则引擎服务

    2024-06-09 16:48:02       33 阅读
  4. AI学习的基础理论路径

    2024-06-09 16:48:02       35 阅读
  5. es6 proxy的作用和用法

    2024-06-09 16:48:02       38 阅读
  6. 简单记录个python国内镜像源

    2024-06-09 16:48:02       40 阅读
  7. Spring Boot配置MySQL数据库连接数

    2024-06-09 16:48:02       37 阅读
  8. vue路由缓存

    2024-06-09 16:48:02       47 阅读
  9. 力扣第185题:部门工资前三高的员工

    2024-06-09 16:48:02       30 阅读
  10. 手机UI设计中的按钮状态包含哪几种

    2024-06-09 16:48:02       31 阅读