unity2D学习(11)角色受伤弹开、用UI Text显示血量

1 角色受伤弹开

想法:角色如果碰撞到敌人,就会显示受伤动画,且受伤后会向和敌人碰撞的相反方向弹开。

动画转移:

  • idle->hurt:injured为true
  • hurt->idle:injured为false,ground为true
  • jump->hurt:injured为true
  • run->hurt:injured为true

unity2D学习(11)角色受伤弹开、用UI Text显示血量

OnTriggerEnter2D 和OnCollisionEnter2D介绍

作用:都是碰撞/接触到会自动触发的函数

区别:

  • OnTriggerEnter2D:取消物理碰撞,在触发时执行。
  • OnCollisionEnter2D:造成物理碰撞,在碰撞时执行。

使用条件:

  • OnTriggerEnter2D:双方都有碰撞体,运动一方必须是刚体,至少一方勾选Trigger触发器。
  • OnCollisionEnter2D:双方都有碰撞体,运动一方必须是刚体,双方都不可勾选Trigger触发器。

这里使用OnCollisionEnter2D,因为角色碰撞到敌人受伤,需要造成物理碰撞。

Tag和Layer的介绍

作用:都是对GameObject进行分类

区别:

  • Tag:是字符串。用于定义一类事物,如子弹、敌人之类的,方便查找游戏物件,以及在触碰时判断游戏物件。
  • Layer:是个32位的值,最多只有32个Layer。和物理系统有关,让 Camera 指定哪些物件要被画出来、让 Light 指定哪些物件要被照明、让物理射线确认哪些物件要被侦测到。

这次的和敌人碰撞属于触碰时判断游戏物件,所以采用了Tag来判断触碰的对象是不是敌人。

为之前设置Crab敌人的Tag设置为Enemy。

unity2D学习(11)角色受伤弹开、用UI Text显示血量

受伤弹开

思路:受伤弹开,要朝着碰撞的相反方向弹开。

方向切换代码:

    void Flip()
    {
        face = (face == 1) ? -1 : 1;//在墙上跳出的话,动画要和原来相反
        transform.localScale = new Vector2(face, 1);//切换方向
    }

判断碰撞方向,选择动画是否反转:

    void AccordingDirectionFlip(Collision2D collision)//根据敌人方向,安排玩家转向
    {
        if (collision != null)//如果玩家出现视野中
        {
            int direction;
            if (collision.transform.position.x < transform.position.x)
            {
                direction = -1;//玩家在敌人的左边
            }
            else
            {
                direction = 1;//玩家在敌人的右边
            }
            if (direction != face)//表示玩家朝向和敌人相对方位不一致
            {
                //Debug.Log(direction);
                Flip();
            }
        }
    }

受伤反弹:

    void Hurt(Collision2D collision)//受伤代码入口
    {
        onHurt = true;
        AccordingDirectionFlip(collision);
        rig.velocity = new Vector2(face * moveForce * Time.deltaTime, jumpForce * Time.deltaTime);//反弹
    }

在OnCollisionEnter2D里面添加触发的内容:

    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            Hurt(collision);
        }
    }

游戏效果:

unity2D学习(11)角色受伤弹开、用UI Text显示血量

关于动画切换的代码可以在文章末尾总代码中查看,因为牵扯到之前的代码有点多,思路主要是添加了一个Bool类型OnHurt来判断是否在受伤状态。

2 入门UI显示血量

想法:角色受伤后会扣除一定的血量,然后这些血量用UI Text在画面中显示。

设置UI显示血量:

新建UI画布Canvas

unity2D学习(11)角色受伤弹开、用UI Text显示血量

在新建的Canvas下新建两个Text,一个Text用来标识血量,另一个Text用来显示血量数值,分别命名为Hp、HpNumber。

unity2D学习(11)角色受伤弹开、用UI Text显示血量

unity2D学习(11)角色受伤弹开、用UI Text显示血量

修改Text的内容、大小、颜色,到合适游戏的情况。

unity2D学习(11)角色受伤弹开、用UI Text显示血量

把UI Text的显示固定在左上角,先把锚点固定在左上角(下图被红色圆圈圈起来的),因为在画面上拖动的UI Text的位置都是相对于锚点。把下图的Rect Transform方框里面选择固定在左上角,然后拖动之前的建立好的Text到左上角。

unity2D学习(11)角色受伤弹开、用UI Text显示血量

用代码修改血量:

    需要的头文件
    using UnityEngine.UI;
    需要的变量
    private Text HpNumberText;
    private float HP;//角色血量
    初始化的时候获取"HpNumber"的UI Text
    HpNumberText= GameObject.Find("HpNumber").GetComponent<Text>();//获取ui对于的text
    HP=100f;//初始角色血量
    在OnCollisionEnter2D里面进行血量显示的修改
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            HP -= 25f;//碰到一次敌人减去25血量
            HpNumberText.text = HP.ToString();
            Hurt(collision);
        }
    }

3 总代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rig;//刚体
    private Animator Anim;//角色的Animator
    [Header("Layers")]
    public LayerMask groundLayer;//用来开启layer
    [Space]
    [Header("Speed")]
    [SerializeField] private float moveSpeed=360f;//移动速度
    private float climbSpeed=100f;//爬行速度
    [Space]
    [Header("Force")]
    private float moveForce=150f;//移动力
    private float jumpForce=575f;//跳跃力
    [Space]
    [Header("Frequency")]
    private int jumpMax=2;//跳跃次数的上线
    private int jumpNum=0;//当前跳跃的次数
    [Space]
    [Header("Booleans")]
    [SerializeField] private bool falling = false;//用来标记是否是下落状态
    [SerializeField] private bool onWall;//是否在墙上
    [SerializeField] private bool onJumping;//是否正在跳跃中
    [SerializeField] private bool onGround;//是否正在地上
    [SerializeField] private bool onHurt;//是否受伤
    [Space]
    [Header("Collision")]
    private float collisionRadius = 0.15f;//碰撞半径
    public Vector2 bottomOffset, rightOffset, leftOffset;//下左右相对于角色中心的二维向量
    private Collider2D coll;//角色的碰撞器
    [Space]
    [Header("UI")]
    private Text HpNumberText;
    [Space]
    private float face;//记录角色朝向
    private float HP;//角色血量
    
    //初始化
    void Start()
    {
        rig = GetComponent<Rigidbody2D>();//获取主角刚体组件
        Anim = GetComponent<Animator>();//获取主角动画组件
        coll = GetComponent<Collider2D>();//获取角色碰撞器
        HpNumberText= GameObject.Find("HpNumber").GetComponent<Text>();//获取ui对于的text
        groundLayer = 1 << 8;//开启Ground的layer层,Ground在layer8
        onWall = false;//初始不在墙上
        onJumping = false;//初始不正在跳跃
        onGround = true;//初始在地上
        face = 1;//初始朝向向右边
        moveSpeed = 360f;//移动速度
        HP = 100f;
        bottomOffset = new Vector2(0,-1.6f);
        rightOffset = new Vector2(0.61f,-1.3f);
        leftOffset = new Vector2(-0.72f,-1.3f);
        onHurt = false;
    }
    //固定的时间间隔执行,不受游戏帧率的影响
    void FixedUpdate()
    {
        Movement();
        changeAnimator();
    }
    //控制移动
    void Movement()
    {
        Debug.Log("sfjalsf");
        //当按下你设置的建则返回一个有加速度的值,从1或者-1 过渡到0
        //得到正数对应正半轴,负数对应负半轴的方向
        float moveMultiple = Input.GetAxis("Horizontal");
        //当按下你设置好的键盘就会返回1和-1这两个值
        float faceDirection = Input.GetAxisRaw("Horizontal");
        //获取纵轴
        float verticalMove = Input.GetAxis("Vertical");
        if (onGround)//在地上
        {
            move(moveMultiple, faceDirection);//左右移动
            if (touchWall())//是否可爬墙,碰到墙
            {
                if (verticalMove>0)//是否有爬墙的行为,按向上键
                {
                    onGround = false;
                    onWall = true;
                    Climb(verticalMove);
                }
            }
            if (Input.GetButtonDown("Jump"))
            {
                onGround = false;
                onJumping = true;
                Jump();
            }
        }
        else if (onJumping)//在空中跳跃
        {
            if (touchWall())//可以爬墙
            {
                onJumping = false;//关闭现在状态
                onWall = true;//开启下一个状态
                Climb(verticalMove);//爬行
                jumpNum = 0;//跳跃次数清零
            }
            else if (Input.GetButtonDown("Jump"))//二段跳
            {
                Jump();
            }
        }
        else if (onWall)//在墙上
        {
            if(Input.GetButtonDown("Jump"))
            {
                onWall = false;
                onJumping = true;
                Flip();
                Jump();
                
            }
            else if (touchWall())//如果碰到墙,就说明还在爬
            {
                Climb(verticalMove);
            }
            else//如果说爬到了末端,就可以有跳跃行为,不然的话难爬到地面
            {
                onWall = false;
                onJumping = true;
                Jump();
            }
        }
    }
    void Flip()//翻转
    {
        face = (face == 1) ? -1 : 1;//在墙上跳出的话,动画要和原来相反
        transform.localScale = new Vector2(face, 1);
    }
    bool isGround()//判断是否碰地
    {
        return Physics2D.OverlapCircle((Vector2)transform.position + bottomOffset, collisionRadius, groundLayer);//判断是否碰到地面
    }
    bool touchWall()//判断是否碰到墙
    {
        return Physics2D.OverlapCircle((Vector2)transform.position + rightOffset, collisionRadius, groundLayer)
            || Physics2D.OverlapCircle((Vector2)transform.position + leftOffset, collisionRadius, groundLayer); 
    }
    void move(float moveMultiple,float faceDirection)//移动代码
    {
        //角色左右移动
        if (moveMultiple != 0)
        {
            //velocity表示速度,Vector表示向量
            rig.velocity = new Vector2(moveMultiple * moveSpeed * Time.deltaTime, rig.velocity.y);//输入x,y向量,数值*方向
        }
        //角色朝向修改
        if (faceDirection != 0)
        {
            //Scale,代表缩放,Localscale代表的是当前物体相对于父物体的缩放,通过正负不修改大小来实现左右朝向修改
            transform.localScale = new Vector2(faceDirection, 1);
            face = faceDirection;//face参数的获取,在每次按下左右按键的时候获取
        }
    }
    void Jump()//跳跃代码
    {
        if(jumpNum < jumpMax)
        {
            rig.velocity = new Vector2(face*moveForce*Time.deltaTime, jumpForce * Time.deltaTime);
            jumpNum++;
        }   
    }
    void Hurt(Collision2D collision)//受伤
    {
        onHurt = true;
        AccordingDirectionFlip(collision);
        rig.velocity = new Vector2(face * moveForce * Time.deltaTime, jumpForce * Time.deltaTime);
    }
    void AccordingDirectionFlip(Collision2D collision)//根据敌人方向,安排玩家转向
    {
        if (collision != null)//如果玩家出现视野中
        {
            int direction;
            if (collision.transform.position.x < transform.position.x)
            {
                direction = -1;//玩家在敌人的左边
            }
            else
            {
                direction = 1;//玩家在敌人的右边
            }
            if (direction != face)//表示方向不一致
            {
                //Debug.Log(direction);
                Flip();
            }
        }
    }
    void Climb(float verticalMove)//爬墙代码
    {
        rig.velocity = new Vector2(rig.velocity.x,climbSpeed * verticalMove* Time.deltaTime);//输入x,y向量,数值*方向
    }
    void changeAnimator()//动画切换
    {
        if (onGround)
        {
            Anim.SetFloat("speed", Mathf.Abs(rig.velocity.x));//速度是向量
            if (onHurt)
            {
                Anim.SetBool("injured", true);
                //Anim.SetBool("ground", false);
                onGround = false;
            }
        }
        if (onWall)//如果在墙上
        {
            Anim.SetBool("wall", true);
            if (Anim.GetBool("ground") == false)
            {
                Anim.SetBool("ground", true);
            }
        }
        else
        {
            Anim.SetBool("wall", false);
        }
        if (onJumping)
        {
            if (onHurt)
            {
                Anim.SetBool("injured", true);
                jumpNum = 0;//当前跳跃次数清零
                falling = false;
                onJumping = false;
            }
            else if (Anim.GetBool("ground"))
            {
                falling = false;
                Anim.SetBool("ground", false);
            }
            else 
            {
                if (falling&&isGround())
                {
                    Anim.SetBool("ground", true);
                    jumpNum = 0;//落地的话,当前跳跃次数清零
                    falling = false;
                    onJumping = false;
                    onGround = true;
                }
                else if(rig.velocity.y < 0)
                {
                    falling = true;
                }
            }

        }
        if (onHurt)
        {
            if (falling && isGround())
            {
                Anim.SetBool("injured", false);
                Anim.SetBool("ground", true);
                falling = false;
                onGround = true;
                onHurt = false;
            }
            else if (rig.velocity.y < 0)
            {
                falling = true;
            }
        }
    }
    void OnDrawGizmos()//绘制辅助线
    {
        Gizmos.color = Color.red;//辅助线颜色
        //绘制圆形辅助线
        Gizmos.DrawWireSphere((Vector2)transform.position + bottomOffset, collisionRadius);
        Gizmos.DrawWireSphere((Vector2)transform.position + rightOffset, collisionRadius);
        Gizmos.DrawWireSphere((Vector2)transform.position + leftOffset, collisionRadius);
    }
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            HP -= 25f;//碰到一次敌人减去25血量
            HpNumberText.text = HP.ToString();
            Hurt(collision);
        }
    }
}

 

上一篇:打砖游戏,详解每一行代码,历经三个小时解析,初学可看


下一篇:VIVADO时钟IP核的功能验证