当前位置:   article > 正文

游戏中AI自由寻路,追随玩家,绕开障碍物(基于unity实现)_unity 前方绕路

unity 前方绕路

游戏中AI自由寻路,追随玩家,绕开障碍物(基于unity实现)

前言

本博客中采用的寻路算法主要为A星算法,A星算法主要实现代码借鉴他人博客(忘记原文地址了,如果原作者刚好看到可以留言,博主将注明原文出处,若涉及侵权请联系博主核实后将立马删除)A星算法具体原理不在本文章描述范围, 若想深入学习算法原理可自行搜索关键字:A星算法。
  • 1

主要实现功能

  1. AI 在将以自身为原点,生成自定义大小的正方形为检测范围;
  2. AI 在自由寻路状态时,随机选取正方形边缘上的一点为目标点,朝目标点移动,到达目标点时将再次选取随机点作为目标点进行移动;
  3. AI生成的地图中每个地图元素都会在成时发射一条向上2米的射线检测,用于检测障碍物(AI定义为1米身高,超过2米的障碍物并不会影响AI前进);
  4. 地图元素采用对象池模式,每次敌人切换目标时地图元素并不用重新生成,而是初始化后继续使用;
  5. AI在追随模式时,地图所有元素将与目标点做距离判定,将距离玩家最近的元素作为目标点,当AI到达目标点时若还为玩家位置将再次作出判定,直到接近玩家为止;
  6. 鼠标左键为自由寻路模式,右键为追随模式,游戏时可自行控制查看效果。

主要代码分析

  1. AI控制脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
    //内部单例
    private static Enemy Instance = null;
    //路径
    private List<Node> path = new List<Node>();
    //起点
    private GameObject start;
    //目标
    private GameObject end;
    //开始寻路
    public bool startPathfinding = true;
    //克隆体
    public GameObject cube;
    //行
    public int row;
    //计时器
    public float timer = 0;
    //地图
    private Dictionary<Vector3, Node> map = new Dictionary<Vector3, Node>();
    //圆点
    private GameObject dot;
    //辅助线
    private LineRenderer line = null;
    //移动点数量
    public int pathCount = 0;

    /// <summary>
    /// 单利访问
    /// </summary>
    public static Enemy instance
    {
        get
        {
            return Instance;
        }
    }


    private void Awake()
    {
        Instance = this;

        //辅助线
        line = new GameObject("Line").AddComponent<LineRenderer>();
        line.startWidth = 0.1f;
        line.endWidth = 0.1f;

        dot = new GameObject("Dot");
    }


    private void Update()
    {
        //测试
        if (Input.GetMouseButtonDown(0)) AutoFindWay();
        if (Input.GetMouseButtonDown(1)) PursueTarget();

        if (startPathfinding)
        {
            if (path.Count > 0)
            {
                transform.position = Vector3.MoveTowards(transform.position, path[pathCount].transform.position, Time.deltaTime);

                transform.LookAt(path[pathCount].transform.position);

                if (Vector3.Distance(transform.position, path[pathCount].transform.position) <= 0)
                {
                    pathCount--;
                }
            }
        }
        else
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out RaycastHit hit) && hit.collider != null && hit.collider.GetComponent<Node>() != null)
            {
                path.Clear();
                FindTargetPath(hit.collider.gameObject);
                startPathfinding = true;
            }
        }
    }


    /// <summary>
    /// 自动寻路
    /// </summary>
    public void AutoFindWay()
    {
        path.Clear();
        NewPath();
        startPathfinding = true;
    }


    /// <summary>
    /// 追击目标
    /// </summary>
    public void PursueTarget()
    {
        path.Clear();
        FindTargetPath(FindObjectOfType<Player>().gameObject);
        startPathfinding = true;
    }



    /// <summary>
    /// 新路线
    /// </summary>
    private void NewPath()
    {
        print("寻找新路径 >>>>>>>>>>");
        //原点到两边距离相等
        if (row % 2 == 0) row++;
        //圆点位置等于自身位置
        dot.transform.position = transform.position;
        //以自身为圆点形成坐标坐标系
        MovingRangeFormationPoint(map,row);
        //设置地图
        NodeManager.Instance.getMap(dot.GetComponentsInChildren<Node>());
        //起点
        start = map[FindRecently(map, transform.transform)].gameObject;
        //目标点
        end = map[EdgeGetTarget(start.transform)].gameObject;
        //延迟0.5s寻路
        Invoke("FindWay", 0.5f);
    }


    /// <summary>
    /// 找到目标
    /// </summary>
    public void FindTargetPath(GameObject target)
    {
        print("以距离目标最近的点作为路径 >>>>>>");
        //原点到两边距离相等
        if (row % 2 == 0) row++;
        //圆点位置等于自身位置
        dot.transform.position = transform.position;
        //以自身为圆点形成坐标坐标系
        MovingRangeFormationPoint(map,row);
        //设置地图
        NodeManager.Instance.getMap(dot.GetComponentsInChildren<Node>());
        //起点
        start = map[FindRecently(map, transform.transform)].gameObject;
        //目标点
        end = map[FindRecently(map,target.transform)].transform.gameObject;
        //延迟1s寻路
        Invoke("FindWay", 0.5f);
    }


    /// <summary>
    /// 以自身为圆点形成坐标坐标系
    /// </summary>
    private void MovingRangeFormationPoint(Dictionary<Vector3, Node> map, int row)
    {
        print("以自身为圆点形成坐标坐标系");
        //以自身为原点
        Vector3 centerPos = dot.transform.position - new Vector3(row * 0.5f - 0.5f, 0, row * 0.5f - 0.5f);
        //复用之前的对象
        if (map.Count > 0)
        {
            //清空障碍物
            NodeManager.Instance.CloseObstacle();
            //下标
            int index = 0;
            //地图上的点(重复利用)
            List<Node> points = new List<Node>();
            //找出之前的点
            foreach (Vector3 i in map.Keys)
            {
                //坐标初始化
                map[i].Init();
                //加入对象池列表
                points.Add(map[i]);
            }
            //清空地图
            map.Clear();

            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < row; j++)
                {
                    //正方形坐标
                    Vector3 pos = new Vector3(centerPos.x + i, 0, centerPos.z + j);
                    //点的位置重新赋值
                    points[index].transform.position = pos;
                    //加入地图
                    map.Add(pos, points[index]);
                    //下一个点
                    index++;
                }
            }
        }
        else
        {
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < row; j++)
                {
                    Vector3 pos = new Vector3(centerPos.x + i, 0, centerPos.z + j);
                    //正方形坐标
                    GameObject game = Instantiate(cube, pos, Quaternion.identity);
                    //将地图设置为圆点的之物体
                    game.transform.SetParent(dot.transform);
                    //点坐标位置
                    game.name = pos.ToString();
                    //添加至地图
                    map.Add(pos, game.GetComponent<Node>());
                    //坐标初始化
                    map[pos].Init();
                }
            }
        }
    }


    /// <summary>
    /// 寻找路线
    /// </summary>
    public void FindWay()
    {
        if (end.GetComponent<Node>().nObstacle)
        {
            NewPath();
            Debug.LogError("目标为障碍物,重新寻路");
            return;
        }

        //路径
        path = AStar.FindPath(start.GetComponent<Node>(), end.GetComponent<Node>());
        //画辅助线
        line.positionCount = path.Count;

        for (int i = 0; i < path.Count; i++)
        {
            line.SetPosition(i, path[i].gameObject.transform.position);
        }

        //移动路径大小
        pathCount = path.Count - 1;
    }


    /// <summary>
    /// 边缘获取目标位置
    /// </summary>
    private Vector3 EdgeGetTarget(Transform dot)
    {
        List<Vector3> squarePoints = new List<Vector3>();

        int width = (row - 1) / 2;

        for (int j = row - 1; j >= 0; j--)
        {
            squarePoints.Add(dot.position + new Vector3(width - j, 0, width));
        }

        for (int j = 1; j < row; j++)
        {
            squarePoints.Add(dot.position + new Vector3(width, 0, width - j));
        }

        for (int j = 1; j < row; j++)
        {
            squarePoints.Add(dot.position + new Vector3(width - j, 0, -width));
        }

        for (int j = row - 2; j > 0; j--)
        {
            squarePoints.Add(dot.position + new Vector3(-width, 0, width - j));
        }

        int randomTarget = Random.Range(0, squarePoints.Count - 1);

        return squarePoints[randomTarget];

    }


    /// <summary>
    /// 寻找地图最近的点
    /// </summary>
    /// <param name="target"></param>
    /// <returns></returns>
    private Vector3 FindRecently(Dictionary<Vector3, Node> map, Transform target)
    {
        List<Vector3> dos = new List<Vector3>();
        //找出地图上的点
        foreach (Vector3 i in map.Keys)
        {
            dos.Add(i);
        }
        //寻找距离玩家最近的点
        Vector3 miniDis = dos[0];

        for (int i = 1; i < dos.Count; i++)
        {   //找出距离玩家最近位置点
            if (Vector3.Distance(miniDis, target.position) > Vector3.Distance(dos[i], target.position))
            {
                miniDis = dos[i];
            }
        }
        return map[miniDis].transform.position;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  1. 地图元素管理类
using System.Collections.Generic;
using UnityEngine;

public class NodeManager : MonoBehaviour
{
    private static NodeManager instance = null;

    public static NodeManager Instance
    {
        get
        {
            if (instance == null)
                instance = FindObjectOfType(typeof(NodeManager)) as NodeManager;
            if (instance == null)
                Debug.LogError("Could not find a NodeManager. Please add one NodeManager in the scense");
            return instance;
        }
    }

    //地图坐标
    public Node[,] nodes;

    /// <summary>
    /// 障碍物列表
    /// </summary>
    public List<GameObject> obstacleList = new List<GameObject>();


    //设置为障碍物
    public void SetObstacle(GameObject obs)
    {
        if (obstacleList.Contains(obs))
            return;
        //障碍物列表
        obstacleList.Add(obs);
        //设置障碍物
        obs.GetComponent<Node>().MarkAsObstacle();
    }


    /// <summary>
    /// 清理障碍物
    /// </summary>
    public void CloseObstacle()
    {
        foreach (GameObject i in obstacleList)
        {
            i.GetComponent<Renderer>().material.color = Color.white;
        }

        obstacleList.Clear();

        print("清理障碍物");
    }



    //得到地图
    public void getMap(Node[] games)
    {
        int index = -1;
        nodes = new Node[Enemy.instance.row, Enemy.instance.row];
        for (int i = 0; i < Enemy.instance.row; i++)
        {
            for (int j = 0; j < Enemy.instance.row; j++)
            {
                index++;
                nodes[i, j] = games[index];
            }
        }
    }


    /// <summary>
    /// 得到附近节点
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    public List<Node> GetNeighbours(Node node)
    {
        //附近节点
        List<Node> neighbours = new List<Node>();

        for (int i = 0; i < Enemy.instance.row; i++)
        {
            for (int j = 0; j < Enemy.instance.row; j++)
            {
                if(nodes[i,j] == node)
                {

                    neighbours = AssignNeighbours(i, j + 1, neighbours);//up

                    //neighbours = AssignNeighbours(i + 1, j + 1, neighbours);//up + right

                    neighbours = AssignNeighbours(i + 1, j, neighbours);//right

                    //neighbours = AssignNeighbours(i + 1, j - 1, neighbours);//right + down

                    neighbours = AssignNeighbours(i, j - 1, neighbours);//down

                    //neighbours = AssignNeighbours(i - 1, j - 1, neighbours);//down + left

                    neighbours = AssignNeighbours(i - 1, j, neighbours);//left

                    //neighbours = AssignNeighbours(i - 1, j + 1, neighbours);//left + up
                }
            }
        }
        return neighbours;
    }

    /// <summary>
    /// 分配的临近点
    /// </summary>
    /// <param name="row"></param>
    /// <param name="col"></param>
    /// <param name="neighbours"></param>
    /// <returns></returns>
    private List<Node> AssignNeighbours(int row, int col, List<Node> neighbours)
    {
        //
        if (row >= 0 && col >= 0 && row < Enemy.instance.row && col < Enemy.instance.row)
        {
            //不属于障碍物
            if(!obstacleList.Contains(nodes[row, col].gameObject))
            {
                neighbours.Add(nodes[row, col]);
            }
        }
    
        return neighbours;
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  1. 地图元素类
using System;
using UnityEngine;
using UnityEngine.UI;

public class Node : MonoBehaviour, IComparable
{
    /// <summary>
    /// 节点到总成本  G
    /// </summary>
    public float nodeTotalCost = 1.0f;

    /// <summary>
    /// 估计成本     F
    /// </summary>
    public float estimateCost = 0.0f;

    /// <summary>
    /// 路径的父节点
    /// </summary>
    public Node nParent;

    /// <summary>
    /// 是否障碍物
    /// </summary>
    public bool nObstacle = false;

    /// <summary>
    /// 初始化
    /// </summary>
    public void Init()
    {
        nodeTotalCost = 1.0f;
        estimateCost = 0.0f;
        nObstacle = false;
        nParent = null;

        //向上2米检测是否有障碍物
        if (Physics.Raycast(transform.position, transform.up, out RaycastHit hit, 2))
        {
            //加入障碍物列表
            NodeManager.Instance.SetObstacle(gameObject);
            //画出辅助线
            Debug.DrawRay(transform.position, transform.up * 2, Color.red, 10);
        }
    }

    /// <summary>
    /// 设置为障碍物
    /// </summary>
    public void MarkAsObstacle()
    {
        nObstacle = true;
    }



    /// <summary>
    /// 对比路径成本
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public int CompareTo(object obj)
    {
        Node node = (Node)obj;
        if(estimateCost < node.estimateCost)
              return -1;
        else if(estimateCost > node.estimateCost)
            return 1;
        else
            return 0;

    }

    public override string ToString()
    {
        return transform.position.x + "," + transform.position.y;
    }

    /// <summary>
    /// 显示大小
    /// </summary>
    public void ShowCost()
    {
        var text = GetComponentInChildren<Text>();
        if (nObstacle)
        {
            text.text = "障碍物";
            text.color = Color.red;
        }
        else {
            text.text = "F: " + estimateCost.ToString("F2") + "\n" +
                        "G: " + nodeTotalCost.ToString("F2") + "\n";
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  1. A星算法主要实现类
using System.Collections.Generic;
using UnityEngine;

public class AStar : MonoBehaviour
{
    //开启队列
    public static PriorityQueue openQueue;
    //关闭队列
    public static PriorityQueue closeQueue;

    //触发式评估大小
    private static float HeuristicEstimateCost(Node currentNode, Node goalNode)
    {
        //返回两个节点的距离
        return Vector3.Distance(currentNode.transform.position, goalNode.transform.position);
    }

    /// <summary>
    /// 寻找路径
    /// </summary>
    /// <param name="start">开始位置</param>
    /// <param name="goal">目标位置</param>
    /// <returns></returns>
    public static List<Node> FindPath(Node start, Node goal)
    {
        openQueue = new PriorityQueue();
        //天际节点
        openQueue.Add(start);
        //初始化成本为0
        start.nodeTotalCost = 0;
        //估计成本等于自身到目标的位置
        start.estimateCost = HeuristicEstimateCost(start, goal);


        closeQueue = new PriorityQueue();
        //起始节点
        Node node = null;
        //打开队列长度大于0
        while (openQueue.Count != 0)
        {
            //得到起点
            node = openQueue.First();
            //判断起点是否等于目标点
            if(node == goal)
            {
                //node.GetComponentInChildren<Text>().text = "目标点";
                print("寻路完毕");
                break;
            }

            //得到周围的位置
            List<Node> neighbours = NodeManager.Instance.GetNeighbours(node);

            foreach(Node neighbour in neighbours)
            {
                //不在关闭队列
                if (!closeQueue.Contains(neighbour))
                {
                    //节点总成本=节点初始节点 + 附近点到起点距离
                    neighbour.nodeTotalCost = node.nodeTotalCost + HeuristicEstimateCost(neighbour,node);
                    //估计成本 = 附近点到目标点距离 + 节点总成本
                    neighbour.estimateCost = HeuristicEstimateCost(neighbour, goal) + neighbour.nodeTotalCost;
                    //附近节点的父物体等于起点
                    neighbour.nParent = node;
                    //不在开启队列
                    if (!openQueue.Contains(neighbour))
                    {
                        //加入开启队列
                        openQueue.Add(neighbour);
                    }
                }
            }
            //起点加入关闭队列
            closeQueue.Add(node);
            //起点从打开队列移除
            openQueue.Remove(node);
        }

        if (node != goal)
        {
            Debug.LogError("找不到路径");
        }

        return CalculatePath(node);
    }

    /// <summary>
    /// 计算路径
    /// </summary>
    /// <param name="node"></param>
    /// <returns>计算后得到的路径</returns>
    private static List<Node> CalculatePath(Node node)
    {
        //路径列表
        List<Node> path = new List<Node>();
        while (node != null)
        {
            //节点加入路径列表
            path.Add(node);
            //设置父物体
            node = node.nParent;
        }
        return path;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

游戏运行效果

场景配置图

游戏场景配置图

游戏运行效果

在这里插入图片描述
在这里插入图片描述

动手操作

打开unity,新建一个3D项目,将 AI自动寻路+绕路+追随.unitypackage文件导入项目,打开Game场景,点击开始运行就可以看到效果了!
  • 1

AI自动寻路+绕路+追随.unitypackage
资源提取码: 7t1p

结语

如果你觉得文章还不错就点点关注吧!
如果你有更好的提议欢迎评论区留言或者私信!
如果你想在第一时间里了解更多关于我的作品,或者想了解编程界其他技术,请扫码关注个人微信公众号,公众号留言将持续带来更新!
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/95196
推荐阅读
相关标签
  

闽ICP备14008679号