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);//绘制圆形
    }

}

 

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