unity2D學習(9)爬牆、跳躍修改、組織Inspector中的屬性顯示

1 準備工作

我把cling錯看成了climb,所以做了個爬牆的功能。這裏把cling動畫就加入到動態切換裏面,然後添加一個參數Wall(Boolean)控制。

參數判斷:

  • jump->cling:wall爲true,在牆上。
  • cling->jump:wall爲false,不在牆上;ground爲false,不在地上。
  • idle->cling:wall爲true,在牆上。
  • cling->idle:wall爲false,不在牆上。
  • run->cling:wall爲true,在牆上。

 

2 爬牆

總代碼的思路:把角色分爲在地上、在牆上、空中三種狀態。在地上的時候,可以移動、爬牆、跳躍;在空中的時候,可以爬牆、二段跳;在牆上的時候,可以上下爬、跳躍。至於爬牆的思路,只需要修改y軸的座標就好了。(總代碼會在本文最後給出)

遇到的問題:

  • 判斷左右碰撞,且要和下面碰撞做區分:搜了很多資料,最終決定採用Physics2D.OverlapCircle來判斷碰撞的方向。

Physics2D.OverlapCircle介紹

作用:檢查對撞機是否落在圓形區域內(用來設置檢測碰撞點,檢測碰撞可以更詳細)。

public static Collider2D OverlapCircle(Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);

point 圓心
radius 半徑
layerMask 檢測的特定Layer的對象
minDepth z軸大於或等於此值的對象
maxDepth z軸小於或等於此值的對象

Gizmos類介紹

Gizmos類:Gizmos用於Scene中給出一個可視化的調試或輔助設置。

作用:爲了讓設置碰撞點範圍可見,方便可視化調整。

注意:

  • Gizmos繪製必須在OnDrawGizmos或OnDrawGizmosSelected函數中完成。
  • OnDrawGizmos隨程序啓動運行,會在每一帖被調用;OnDrawGizmosSelected當鼠標點擊時候運行

這次代碼要用到的繪製函數:

  • Gizmos.color = Color.red;//設置顏色
  • Gizmos.DrawWireSphere((Vector2)向量, 半徑);//設置圓形

碰撞點繪製代碼

    public Vector2 bottomOffset, rightOffset, leftOffset;//下左右相對於角色中心的二維向量座標
    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);
    }

上面的座標參數設置成了public,這樣就可以在inspector裏面可以觀察到參數,根據效果來修改。

判斷碰撞方向代碼

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

3 跳躍修改

問題:

  • 跳躍後橫向速度不固定:之前寫的跳躍,跳躍起來之後還能夠左右移動,就算不能去掉左右移動,也還是會收到初始跳躍的橫向速度影響。
  • 爬牆時跳躍後方向要修改:爬牆的方向如果是向右,那麼跳躍的話方向就會向左。

代碼思路:

  • 跳躍後橫向速度固定:x方向的速度修改,按照之前跳躍y的速度修改方法就好了。
  • 爬牆跳躍方向修改:用一個face參數(1表示右邊,-1表示左邊)記錄當前的角色朝向,然後如果跳躍就用face來改變角色朝向,速度=數值*face相反數值。至於face參數的獲取,在每次按下左右按鍵的時候獲取。

跳躍的速度變化核心代碼

rig.velocity = new Vector2(face*moveForce*Time.deltaTime, jumpForce * Time.deltaTime);

爬牆跳躍方向核心代碼

    參數face
    private float face=1;//記錄角色朝向,初始爲向右
    參數face修改
    float faceDirection = Input.GetAxisRaw("Horizontal");
    if (faceDirection != 0)//按下按鍵,左邊爲-1,右邊爲1,不按就是0
    {
        face = faceDirection;//face參數的獲取,在每次按下左右按鍵的時候獲取
    }
    爬牆跳躍時候,進行的方向修改
    face = (face == 1) ? -1 : 1;//在牆上跳出的話,動畫要和原來相反
    transform.localScale = new Vector2(face, 1);

4 組織Inspector中的屬性顯示

涉及到的參數越來越多,需要來組織一下這些參數在Inspector上的顯示。

通過 “Header”、“Tooltip”、“Space”、“HideInInspector” 、“SerializeField”和屬性來組織Inspector中的屬性顯示。

[Header("hello")]:會出現一行字符串“hello”

[Tooltip("hello")]Tooltip:寫在某個變量上方,就可以在instpector面板上點擊到對應的變量後,提示註釋字符串"hello"

[Space]:會在Inspector上的顯示顯示空行

[HideInInspector]:公有變量可以在Inspector上的顯示,所以在定義變量前面加上就可以隱藏顯示了

[SerializeField]:私有變量不能在Inspector上的顯示,所以在定義變量前面加上就可以顯示了

5 最終完整代碼

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

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=100f;//移動力
    private float jumpForce=575f;//跳躍力

    [Space]
    [Header("Frequency")]
    private int jumpMax=2;//跳躍次數的上線
    [SerializeField] private int jumpNum=0;//當前跳躍的次數

    [Space]
    [Header("Booleans")]
    [SerializeField] private bool falling = false;//用來標記是否是下落狀態
    [SerializeField] private bool onWall;//是否在牆上
    [SerializeField] private bool onJumping;//是否正在跳躍中
    [SerializeField] private bool onGround;//是否正在地上

    [Space]
    [Header("Collision")]
    private float collisionRadius = 0.15f;//碰撞半徑
    public Vector2 bottomOffset, rightOffset, leftOffset;//下左右相對於角色中心的二維向量
    private Collider2D coll;//角色的碰撞器

    [Space]
    private float face;//記錄角色朝向
    

    //初始化
    void Start()
    {
        rig = GetComponent<Rigidbody2D>();//獲取主角剛體組件
        Anim = GetComponent<Animator>();//獲取主角動畫組件
        coll = GetComponent<Collider2D>();//獲取角色碰撞器
        groundLayer = 1 << 8;//開啓Ground的layer層,Ground在layer8
        onWall = false;//初始不在牆上
        onJumping = false;//初始不正在跳躍
        onGround = true;//初始在地上
        face = 1;//初始朝向向右邊
        moveSpeed = 360f;//移動速度
    }

    void FixedUpdate()
    {
        Movement();
        changeAnimator();
    }

    //控制移動
    void Movement()
    {
        float moveMultiple = Input.GetAxis("Horizontal");
        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;
                face = (face == 1) ? -1 : 1;//在牆上跳出的話,動畫要和原來相反
                transform.localScale = new Vector2(face, 1);
                Jump();
                
            }
            else if (touchWall())//如果碰到牆,就說明還在爬
            {
                Climb(verticalMove);
            }
            else//如果說爬到了末端,就可以有跳躍行爲,不然的話難爬到地面
            {
                onWall = false;
                onJumping = true;
                Jump();
            }
        }
    }
    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;
        }
    }
    void Jump()//跳躍代碼
    {
        if(jumpNum < jumpMax)
        {
            rig.velocity = new Vector2(face*moveForce*Time.deltaTime, jumpForce * Time.deltaTime);
            jumpNum++;
        }   
    }
    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 (onWall)//如果在牆上
        {
            Anim.SetBool("wall", true);//肯定是碰到牆
            if (Anim.GetBool("ground") == false)//如果是跳躍狀態下碰到牆,就把碰到地面設爲真,方便轉換到idle狀態
            {
                Anim.SetBool("ground", true);
            }
        }
        else
        {
            Anim.SetBool("wall", false);
        }
        if (onJumping)//如果在跳躍中
        {
            if (Anim.GetBool("ground"))//第一次跳
            {
                falling = true;
                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)//如果速度爲下落,則設置下落狀態爲true
                {
                    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);
    }
}

 

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