unity2D學習(10)創建敵人、爲敵人編寫簡單的AI

1 創建敵人

按照之前創建Player角色的方法,一樣創建出敵人,併爲角色添加剛體、碰撞、動畫。具體可以參考我之前的unity2D學習(4)(5),具體的細節就不放在這裏了。

素材包裏面有三種敵人(crab、jumper、octopus),我選擇crab來做示範動畫狀態轉移。把Animation新建在Asserts->Animation->Enemies->Crab裏面,其它類型的敵人以此類推。

參數判斷:

  • Walk->Idie:speed小於0.01f
  • Idie->Walk:speed大於0.01f

2 爲敵人設置簡單ai

我的想法:如果Player角色出現在某一範圍內,敵人就會朝玩家走去碰撞,然後走到邊緣就會停下來。因爲我後面想寫一個玩家碰到敵人就會受傷的效果,所以我希望敵人會主動去碰撞玩家。

思路:

  • 敵人判斷Player是否是在敵人的某個範圍內:可以用Physics2D.OverlapCircle,來判斷角色的碰撞體是否在圓形範圍裏面,然後根據Player在敵人左邊還是右邊來改變方向。

利用Physics2D.OverlapCircle判斷玩家是否進入視野,和上一章unity2D學習(9)中判斷碰牆的方法一樣。

但是這次有點不同就是,如果用Collider2D類型來獲取Physics2D.OverlapCircle就可以得到碰撞對象Collider2D,上一章裏面使用Bool類型來獲取得到就是“是否碰撞到”。還有需要爲Player玩家設置一個Layer層來做判斷用。

    Collider2D isPlayerView()//判斷玩家是否進入了視野
    {
        return Physics2D.OverlapCircle((Vector2)transform.position, collisionRadius,playerLayer);//判斷是否碰到玩家
    }

利用獲取的Collider2D類型來判斷敵人在玩家的那個方向。但是要注意敵人的動畫的朝向初始是朝向左邊的和主角不一樣。敵人初始的橫向向量雖然是正,但是動畫的朝向起始對應的是左邊。

    void AccordingDirectionFlip(Collider2D playerColl)//根據玩家是否出現在視野中,安排敵人轉向
    {
        if (playerColl != null)//如果玩家出現視野中
        {
            int direction;
            if (playerColl.transform.position.x < transform.position.x)
            {
                direction = -1;//玩家在敵人的左邊
            }
            else
            {
                direction = 1;//玩家在敵人的右邊
            }
            if (direction != face)//表示方向不一致
            {
                //Debug.Log(direction);
                Flip();
            }
        }
    }
    void Flip()//翻轉角色方向
    {
        face = (face == 1) ? -1 : 1;
        transform.localScale = new Vector2(face*(-1), 1);//乘-1是因爲初始動畫朝向是朝着左邊的,但是初始座標卻是1,是相反的
    }
  • 敵人跟隨Player會走到邊緣,需要停下:用射線Physics2D.Raycast來判斷是否走到了邊緣。

Physics2D.Raycast介紹

作用:檢查對撞機是否落在射線上。

public static RaycastHit2D Raycast(Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);

origin 二維空間中射線起源的點
direction 表示射線方向的向量
distance 投射射線的最大距離
layerMask 檢測的特定Layer的對象
minDepth z軸大於或等於此值的對象
maxDepth z軸小於或等於此值的對象

設置Physics2D.Raycast的射線方向的向量爲45°(斜着向下),然後檢測的特殊Layer層爲之前創建的Ground的Layer(之前TileMap的Layer層),然後起始源點爲敵人自己的座標。

    涉及到的變量
    Vector2 down = new Vector2(0, -1);//控制射線角度的向量
    private float radialLength = 1.5f;//射線的長度
    beg = transform.position;//射線起點,這個要每帖更新
    核心代碼
    bool isBorder()//判斷是否已經抵達邊境
    {
        //也可以採用Debug的方式可視化射線
        //Debug.DrawLine(beg, beg + (new Vector2(face, 0) + down) * radialLength, Color.red);
        if (!Physics2D.Raycast(beg,new Vector2(face,0)+down, radialLength, LayerMask.GetMask("Ground")))//抵達邊境
        {
            return true;
        }
        return false;
    }

這種方法射線和圈不好調整,爲了方便調整參數,繪製輔助線來可視化。(具體代碼看文章的末尾)

3 總代碼

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

public class EnamyController : MonoBehaviour {

    private Rigidbody2D rig;//剛體
    private Animator Anim;//動畫
    [Header("Layers")]
    public LayerMask playerLayer;//用來開啓layer
    [Space]
    [Header("Collision")]
    private Collider2D coll;//碰撞器
    [SerializeField] private float collisionRadius = 5f;//檢測碰撞半徑
    Vector2 beg;//射線起點
    Vector2 down = new Vector2(0, -1);//控制射線角度的向量
    [SerializeField] private float radialLength = 1.1f;//射線的長度
    [Space]
    [Header("Speed")]
    private float moveSpeed = 100f;//移動速度
    [SerializeField] private float face;//朝向
    
    void Start () {
        rig = GetComponent<Rigidbody2D>();//獲取剛體組件
        Anim = GetComponent<Animator>();//獲取動畫組件
        coll = GetComponent<Collider2D>();//獲取碰撞器
        face = -1;//初始朝向是負的,和角色不同的地方,face-1代表朝向左邊
        playerLayer = 1 << 9;//把玩家Layer放在Layer9
    }

    void FixedUpdate()
    {
        beg = transform.position;
        Collider2D playerColl = isPlayerView();
        if (isBorder())//是否到邊緣
        {
            Debug.Log("hello");
            rig.velocity = new Vector2(0,0);
            AccordingDirectionFlip(playerColl);
        }
        else //不到邊緣就可以移動
        {
            AccordingDirectionFlip(playerColl);
            Move();
        }
    }

    void AccordingDirectionFlip(Collider2D playerColl)//根據玩家是否出現在視野中,安排敵人轉向
    {
        if (playerColl != null)//如果玩家出現視野中
        {
            int direction;
            if (playerColl.transform.position.x < transform.position.x)
            {
                direction = -1;//玩家在敵人的左邊
            }
            else
            {
                direction = 1;//玩家在敵人的右邊
            }
            if (direction != face)//表示方向不一致
            {
                //Debug.Log(direction);
                Flip();
            }
        }
    }
    void Flip()//翻轉角色方向
    {
        face = (face == 1) ? -1 : 1;
        transform.localScale = new Vector2(face*(-1), 1);//乘-1是因爲初始動畫朝向是朝着左邊的,但是初始座標卻是1,是相反的
    }

    bool isBorder()//判斷是否已經抵達邊境
    {
        //也可以採用Debug的方式可視化射線
        //Debug.DrawLine(beg, beg + (new Vector2(face, 0) + down) * radialLength, Color.red);
        if (!Physics2D.Raycast(beg,new Vector2(face,0)+down, radialLength, LayerMask.GetMask("Ground")))//抵達邊境
        {
            return true;
        }
        return false;
    }

    Collider2D isPlayerView()//判斷玩家是否進入了視野
    {
        return Physics2D.OverlapCircle((Vector2)transform.position, collisionRadius,playerLayer);//判斷是否碰到地面
    }

    void Move()//左右移動
    {
        rig.velocity = new Vector2(face*moveSpeed * Time.deltaTime, rig.velocity.y);//輸入x,y向量,數值*方向
    }

    void ChangeAnimator()//動畫狀態轉換
    {
        Anim.SetFloat("speed", Mathf.Abs(rig.velocity.x));//速度是向量
    }

    void OnDrawGizmos()//繪製輔助線
    {
        Gizmos.color = Color.red;//輔助線顏色
        Gizmos.DrawWireSphere((Vector2)transform.position, collisionRadius);//繪製射線
        Gizmos.DrawLine((Vector2)transform.position, (Vector2)transform.position+(new Vector2(face, 0) + down)*radialLength);//繪製圓形
    }

}

 

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