unity2D學習(11)角色受傷彈開、用UI Text顯示血量

1 角色受傷彈開

想法:角色如果碰撞到敵人,就會顯示受傷動畫,且受傷後會向和敵人碰撞的相反方向彈開。

動畫轉移:

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

OnTriggerEnter2D 和OnCollisionEnter2D介紹

作用:都是碰撞/接觸到會自動觸發的函數

區別:

  • OnTriggerEnter2D:取消物理碰撞,在觸發時執行。
  • OnCollisionEnter2D:造成物理碰撞,在碰撞時執行。

使用條件:

  • OnTriggerEnter2D:雙方都有碰撞體,運動一方必須是剛體,至少一方勾選Trigger觸發器。
  • OnCollisionEnter2D:雙方都有碰撞體,運動一方必須是剛體,雙方都不可勾選Trigger觸發器。

這裏使用OnCollisionEnter2D,因爲角色碰撞到敵人受傷,需要造成物理碰撞。

Tag和Layer的介紹

作用:都是對GameObject進行分類

區別:

  • Tag:是字符串。用於定義一類事物,如子彈、敵人之類的,方便查找遊戲物件,以及在觸碰時判斷遊戲物件。
  • Layer:是個32位的值,最多隻有32個Layer。和物理系統有關,讓 Camera 指定哪些物件要被畫出來、讓 Light 指定哪些物件要被照明、讓物理射線確認哪些物件要被偵測到。

這次的和敵人碰撞屬於觸碰時判斷遊戲物件,所以採用了Tag來判斷觸碰的對象是不是敵人。

爲之前設置Crab敵人的Tag設置爲Enemy。

受傷彈開

思路:受傷彈開,要朝着碰撞的相反方向彈開。

方向切換代碼:

    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);
        }
    }

遊戲效果:

關於動畫切換的代碼可以在文章末尾總代碼中查看,因爲牽扯到之前的代碼有點多,思路主要是添加了一個Bool類型OnHurt來判斷是否在受傷狀態。

2 入門UI顯示血量

想法:角色受傷後會扣除一定的血量,然後這些血量用UI Text在畫面中顯示。

設置UI顯示血量:

新建UI畫布Canvas

在新建的Canvas下新建兩個Text,一個Text用來標識血量,另一個Text用來顯示血量數值,分別命名爲Hp、HpNumber。

修改Text的內容、大小、顏色,到合適遊戲的情況。

把UI Text的顯示固定在左上角,先把錨點固定在左上角(下圖被紅色圓圈圈起來的),因爲在畫面上拖動的UI Text的位置都是相對於錨點。把下圖的Rect Transform方框裏面選擇固定在左上角,然後拖動之前的建立好的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);
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章