Unity——游戏AI实例

上文Unity------模拟AI视觉已经实现了敌人视野探测功能,本文来完善敌人AI。

注意:若要阅读此文,务必在阅读完Unity------模拟AI视觉的基础上阅读


效果预展示:

AI敌人追击


接下来用最简单的方式实现敌人的AI状态机。首先,定义敌人的3个状态------待机、进攻和返回。

cs 复制代码
 enum AIState
    {
        Idle,   //待机状态
        Attack,  //进攻状态
        Back,    //返回状态
    } 

然后将Update函数改为状态机的模式,直接用switch-case语句实现

cs 复制代码
  enum AIState
    {
        Idle,   //待机状态
        Attack,  //进攻状态
        Back,    //返回状态
    } 
    AIState state;
 void Update()
    {
       
        switch (state)
        {
            case AIState.Idle:
                {
                    //待机状态。进行实现检测,若发现玩家则进攻
                    FieldOfView();
                }
                break;
                case AIState.Attack:
                {
                    //进攻状态,若离玩家或起点太远,则返回
                }
                break ;
                case AIState.Back:
                {
                    //返回状态
                }
                break;
        }
     }

状态机的原理比较复杂,但只需要用一个switch-case语句就能实现,或者用if语句编写也可以。之后只要把设计思路按部就班地编写成程序代码即可。

  1. 在待机状态下,要不断进行射线检测。如果射线检测发现了玩家,就可以将玩家的引用保存起来,以便后面进攻时使用。需要注意的是,应当将玩家及其子物体的Tag都改为Player,方便判断。
  2. 在进攻状态下,不断向目标位置移动(利用导航系统)。同时检测当前位置与起点或玩家之间的距离,如果距离过远就返回。
  3. 在返回状态下,先朝起点的位置移动,当移动到位后,再转向正面,回到一开始的朝向。有必要一开始就把初始的位置和朝向记录下来,分别是homePos和homeRot。
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIEnemy : MonoBehaviour
{
    Transform target;   //目标角色
    Vector3 homePos;  //起始位置
    Quaternion homeRot;  //起点的朝向 
    UnityEngine.AI.NavMeshAgent agent;
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    public MeshFilter viewMeshFilter;
    List<Vector3> viewVerts;  //定点列表
    List<int> viewIndices;  //定点序号列表
    enum AIState
    {
        Idle,   //待机状态
        Attack,  //进攻状态
        Back,    //返回状态
    } 
    AIState state;   //AI状态机的状态
    void Start()
    {
        Transform view = transform.Find("view");
        viewMeshFilter = view.GetComponent<MeshFilter>();
        agent=GetComponent<UnityEngine.AI.NavMeshAgent>();

        viewVerts = new List<Vector3>();
        viewIndices = new List<int>();
        state = AIState.Idle;

        homePos = transform.position;
        homeRot = transform.rotation;
    }


    void Update()
    {
       
        switch (state)
        {
            case AIState.Idle:
                {
                    //待机状态。进行射线检测,若发现玩家则进攻
                    FieldOfView();
                    if (target != null)
                    {
                        state= AIState.Attack;  //切换状态
                    }
                }
                break;
                case AIState.Attack:
                {
                    //进攻状态,若离玩家或起点太远,则返回
                    agent.SetDestination(target.position);
                    if(Vector3.Distance(transform.position, target.position) > 10)
                    {
                        target = null;
                        state= AIState.Back;
                    }
                    if (Vector3.Distance(transform.position, homePos)>15)
                    {
                        target = null;
                        state = AIState.Back;
                    }
                }
                break ;
                case AIState.Back:
                {
                    //返回状态
                    agent.SetDestination(homePos);
                    if (!agent.hasPath)
                    {
                        //回到起点,匀速转到正面
                        if(Quaternion.Angle(homeRot,transform.rotation)>0.5f)
                        {
                            //逐步向目标角度转动,每次最多转2°
                            Quaternion q = Quaternion.RotateTowards(transform.rotation, homeRot, 2f);
                                transform.rotation = q;
                        }
                        else
                        {
                            state = AIState.Idle;
                        }
                    }
                }
                break;
        }
     }
        void FieldOfView()
        {
            viewVerts.Clear();
            viewVerts.Add(Vector3.zero);  //加入起点坐标,局部坐标系


            //获得最左边那条射线的向量,相对正前方,角度是-45°
            Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
            //依次处理每条射线
            for (int i = 0; i <= viewLines; i++)
            {
                Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
                //角色位置+v,就是射线终点pos
                Vector3 pos = transform.position + v;

                //实际发射射线。注意RayCast的参数,重载很多容易搞错
                RaycastHit hitInfo;
                if (Physics.Raycast(transform.position, v, out hitInfo, viewRadius))
                {
                    //碰到物体,终点改为碰到的点
                    pos = hitInfo.point;
                if (hitInfo.transform.CompareTag("Player"))
                {
                    target=hitInfo.transform;
                }
                }
                //将每个点的位置加入列表,注意转为局部坐标系
                Vector3 p = transform.InverseTransformPoint(pos);
                viewVerts.Add(p);

            }
            //根据顶点绘制模型
            RefreshView();
        }
        void RefreshView()
        {
            viewIndices.Clear();
            //逐个加入三角面,每个三角面都以起点开始
            for (int i = 1; i < viewVerts.Count - 1; i++)
            {
                viewIndices.Add(0);
                viewIndices.Add(i);
                viewIndices.Add(i + 1);
            }
            //填写Mesh信息
            Mesh mesh = new Mesh();
            mesh.vertices = viewVerts.ToArray();
            mesh.triangles = viewIndices.ToArray();
            viewMeshFilter.mesh = mesh;
        }
}
相关推荐
BT-BOX8 小时前
普中开发板基于51单片机贪吃蛇游戏设计
单片机·游戏·51单片机
90后小陈老师9 小时前
Unity教学 项目2 2D闯关游戏
游戏·unity·游戏引擎
噗噗夹的TA之旅10 小时前
Unity Shader 学习20:URP LitForwardPass PBR 解析
学习·unity·游戏引擎·图形渲染·技术美术
nnsix10 小时前
Unity ReferenceFinder插件 多选资源查找bug解决
unity·游戏引擎·bug
gzroy11 小时前
Unity Shader Graph实现全息瞄准器
unity·游戏引擎
90后小陈老师15 小时前
Unity教学 基础介绍
unity·游戏引擎
90后小陈老师15 小时前
Unity教学 项目3 3D坦克大战
3d·unity·游戏引擎
秦奈17 小时前
Unity复习学习随笔(五):Unity基础
学习·unity·游戏引擎
nnsix17 小时前
Unity ReferenceFinder插件 窗口中选择资源时 同步选择Assets下的资源
java·unity·游戏引擎
毕设源码-邱学长17 小时前
【开题答辩全过程】以 基于协同过滤算法的游戏推荐系统的设计与实现为例,包含答辩的问题和答案
游戏