Jxkajsknskjsnskj 2026-04-12 12:46 采纳率: 0%
浏览 4

难题-NPC难题,如何同时保留自动生成地图功能和聪明追踪玩家NPC

那你用团结引擎开发游戏时,你要进行一个AI自动生成的地图迷宫(臨時生成迷宫,玩家走到哪就生成到哪)但是你又要N PC而且要聪明的,NPC又要知道地图该怎么办

img

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-04-12 12:47
    关注

    中午好🌞🌞🌞
    本答案参考通义千问

    在使用团结引擎(Unity)开发游戏时,要实现一个动态生成地图迷宫,同时让NPC能够聪明地追踪玩家,这确实是一个挑战。它涉及到多个技术领域,包括程序化生成(Procedural Generation)路径规划(Pathfinding)AI行为逻辑实时地图更新

    下面我将详细说明如何解决这个难题,并给出具体的解决方案和代码示例。


    一、核心问题分析

    1. 动态生成地图:地图是临时生成的,玩家走到哪就生成到哪。
    2. NPC智能追踪玩家:NPC需要具备导航能力,能根据当前地图进行路径搜索。
    3. NPC知道地图信息:NPC必须拥有地图的认知能力,以便做出合理决策。

    二、解决方案概述

    1. 分块式地图生成(Chunk-based Map Generation)

    • 思想:将地图划分为若干个“区块”或“块”,当玩家进入某个区域时,才生成该区域的地图。
    • 优点
      • 节省内存和计算资源。
      • 实现动态加载与卸载。
    • 实现方式
      • 使用 GridTilemap 来表示每个区块。
      • 玩家移动时,触发地图生成事件。

    2. 网格系统(Grid System)用于路径规划

    • 思想:为整个地图建立一个网格系统,每个格子代表一个可行走区域。
    • 作用
      • 为AI提供统一的坐标参考。
      • 支持A*算法等路径搜索算法。
    • 实现方式
      • 在 Unity 中使用 Tilemap 或自定义网格系统。
      • 每个 NPC 持有当前所在网格的坐标。

    3. AI 行为树 + A 算法*

    • 思想
      • 行为树(Behavior Tree):控制 NPC 的行为逻辑(如巡逻、追击、躲避等)。
      • A 算法*:用于实时路径规划,确保 NPC 可以找到最短路径到达玩家。
    • 关键点
      • NPC 需要实时更新其视野内的地图信息
      • 当新地图生成时,更新网格数据并重新计算路径。

    4. NPC 的地图认知机制

    • 思想:NPC 不仅知道自己的位置,还知道周围已生成的地图结构
    • 实现方式
      • 使用 RaycastLine of Sight 检测玩家是否可见。
      • 使用 局部地图缓存(Local Map Cache)存储已知区域的信息。

    三、具体实现步骤

    步骤 1:创建动态地图生成系统

    using UnityEngine;
    
    public class MapGenerator : MonoBehaviour
    {
        public GameObject tilePrefab;
        public int chunkSize = 10;
        private Dictionary<Vector2Int, GameObject> generatedChunks = new Dictionary<Vector2Int, GameObject>();
    
        void Update()
        {
            Vector2Int playerPosition = GetPlayerChunkPosition();
            if (!generatedChunks.ContainsKey(playerPosition))
            {
                GenerateChunk(playerPosition);
            }
        }
    
        private Vector2Int GetPlayerChunkPosition()
        {
            return new Vector2Int((int)Player.Instance.transform.position.x / chunkSize,
                                  (int)Player.Instance.transform.position.z / chunkSize);
        }
    
        private void GenerateChunk(Vector2Int position)
        {
            // 生成一个包含随机障碍物的迷宫块
            GameObject chunk = new GameObject("Chunk_" + position);
            chunk.transform.position = new Vector3(position.x * chunkSize, 0, position.y * chunkSize);
    
            for (int x = 0; x < chunkSize; x++)
            {
                for (int z = 0; z < chunkSize; z++)
                {
                    if (Random.Range(0, 10) > 5)
                    {
                        Instantiate(tilePrefab, new Vector3(x, 0, z), Quaternion.identity, chunk.transform);
                    }
                }
            }
    
            generatedChunks[position] = chunk;
        }
    }
    

    重点:此代码实现了按需生成地图块的功能,避免一次性加载全部地图。


    步骤 2:建立网格系统(Grid)

    public class GridSystem : MonoBehaviour
    {
        public int cellSize = 1;
        public Transform gridParent;
        public List<GameObject> gridCells = new List<GameObject>();
    
        void Start()
        {
            GenerateGrid();
        }
    
        void GenerateGrid()
        {
            foreach (Transform child in gridParent)
            {
                Destroy(child.gameObject);
            }
    
            gridCells.Clear();
    
            for (int x = 0; x < 100; x++)
            {
                for (int z = 0; z < 100; z++)
                {
                    GameObject cell = new GameObject("Cell_" + x + "," + z);
                    cell.transform.position = new Vector3(x * cellSize, 0, z * cellSize);
                    cell.transform.SetParent(gridParent);
                    gridCells.Add(cell);
                }
            }
        }
    
        public bool IsWalkable(int x, int z)
        {
            // 根据实际地图判断是否可走
            return true; // 示例中默认可走
        }
    }
    

    重点:网格系统为 AI 提供了统一的路径搜索基础。


    步骤 3:实现 A* 算法(路径搜索)

    public class AStarPathfinder
    {
        private GridSystem grid;
        private List<Node> openSet = new List<Node>();
        private HashSet<Node> closedSet = new HashSet<Node>();
        private Node[,] nodes;
    
        public AStarPathfinder(GridSystem grid)
        {
            this.grid = grid;
            nodes = new Node[grid.gridCells.Count, grid.gridCells.Count];
            InitializeNodes();
        }
    
        private void InitializeNodes()
        {
            for (int i = 0; i < nodes.GetLength(0); i++)
            {
                for (int j = 0; j < nodes.GetLength(1); j++)
                {
                    nodes[i, j] = new Node(i, j);
                }
            }
        }
    
        public List<Node> FindPath(int startX, int startZ, int endX, int endZ)
        {
            Node startNode = nodes[startX, startZ];
            Node endNode = nodes[endX, endZ];
    
            openSet.Add(startNode);
    
            while (openSet.Count > 0)
            {
                Node currentNode = GetLowestFCostNode(openSet);
    
                if (currentNode == endNode)
                {
                    return CalculatePath(endNode);
                }
    
                openSet.Remove(currentNode);
                closedSet.Add(currentNode);
    
                foreach (Node neighbor in GetNeighbors(currentNode))
                {
                    if (closedSet.Contains(neighbor)) continue;
                    if (!grid.IsWalkable(neighbor.x, neighbor.z)) continue;
    
                    int newCostToReach = currentNode.gCost + GetDistance(currentNode, neighbor);
                    if (newCostToReach < neighbor.gCost || !openSet.Contains(neighbor))
                    {
                        neighbor.gCost = newCostToReach;
                        neighbor.hCost = GetDistance(neighbor, endNode);
                        neighbor.parent = currentNode;
    
                        if (!openSet.Contains(neighbor))
                        {
                            openSet.Add(neighbor);
                        }
                    }
                }
            }
    
            return null;
        }
    
        private Node GetLowestFCostNode(List<Node> list)
        {
            Node lowestNode = list[0];
            for (int i = 1; i < list.Count; i++)
            {
                if (list[i].fCost < lowestNode.fCost)
                {
                    lowestNode = list[i];
                }
            }
            return lowestNode;
        }
    
        private List<Node> GetNeighbors(Node node)
        {
            List<Node> neighbors = new List<Node>();
            for (int x = -1; x <= 1; x++)
            {
                for (int z = -1; z <= 1; z++)
                {
                    if (x == 0 && z == 0) continue;
    
                    int checkX = node.x + x;
                    int checkZ = node.z + z;
    
                    if (checkX >= 0 && checkX < nodes.GetLength(0) && checkZ >= 0 && checkZ < nodes.GetLength(1))
                    {
                        neighbors.Add(nodes[checkX, checkZ]);
                    }
                }
            }
            return neighbors;
        }
    
        private int GetDistance(Node a, Node b)
        {
            return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.z - b.z);
        }
    
        private List<Node> CalculatePath(Node endNode)
        {
            List<Node> path = new List<Node>();
            Node currentNode = endNode;
    
            while (currentNode != null)
            {
                path.Add(currentNode);
                currentNode = currentNode.parent;
            }
    
            path.Reverse();
            return path;
        }
    
        private class Node
        {
            public int x, z;
            public int gCost, hCost;
            public Node parent;
    
            public Node(int x, int z)
            {
                this.x = x;
                this.z = z;
            }
    
            public int fCost => gCost + hCost;
        }
    }
    

    重点:A* 算法为 NPC 提供了高效路径搜索能力,保证其能聪明地追踪玩家。


    步骤 4:NPC AI 控制逻辑

    public class NPCAI : MonoBehaviour
    {
        public Transform player;
        public float moveSpeed = 2f;
        private AStarPathfinder pathfinder;
        private List<AStarPathfinder.Node> path;
        private int currentWaypoint = 0;
    
        void Start()
        {
            pathfinder = new AStarPathfinder(GameObject.Find("GridSystem").GetComponent<GridSystem>());
        }
    
        void Update()
        {
            if (path == null || currentWaypoint >= path.Count)
            {
                // 寻找新路径
                path = pathfinder.FindPath(
                    (int)transform.position.x, 
                    (int)transform.position.z, 
                    (int)player.position.x, 
                    (int)player.position.z
                );
                currentWaypoint = 0;
            }
    
            if (path != null && currentWaypoint < path.Count)
            {
                Vector3 targetPosition = new Vector3(path[currentWaypoint].x, 0, path[currentWaypoint].z);
                transform.LookAt(targetPosition);
                transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
    
                if (Vector3.Distance(transform.position, targetPosition) < 0.1f)
                {
                    currentWaypoint++;
                }
            }
        }
    }
    

    重点:NPC 会根据当前地图状态,实时寻找最优路径,实现聪明追踪。


    四、总结:关键点归纳

    1. 分块式地图生成:按需生成地图,节省资源。
    2. 网格系统:为 AI 提供统一的路径搜索基础。
    3. A 算法*:实现智能路径规划。
    4. NPC 认知机制:通过局部地图缓存、视野检测等方式,让 NPC 知道地图结构。

    五、建议优化方向

    • 多线程生成地图:提升性能。
    • 更复杂的 AI 行为树:增加 NPC 的复杂行为(如躲藏、攻击等)。
    • 动态更新地图信息:当新区域生成时,及时通知所有 NPC 更新路径。

    如果你希望我进一步扩展某个部分(例如行为树、多 NPC 同步、网络同步等),请告诉我!

    评论

报告相同问题?

问题事件

  • 创建了问题 4月12日