You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity3D中查找距离目标最近的路径点 类Hitman Go游戏开发需求

实现逻辑

因为所有相邻路径点距离相等,直接用广度优先搜索(BFS)计算最短路径即可,BFS天然适配无权图的最短路径查找,也兼容3D空间的坐标计算。
具体步骤:

  • 给每个路径点绑定独立ID和连通关系表,记录该点可直接到达的其他路径点
  • 新增状态标记区分敌人的巡逻状态、警戒移动状态
  • 触发警戒时,以敌人当前所在路径点为起点,警戒目标点为终点运行BFS得到最短路径队列
  • 按路径队列逐点移动,到达目标点后可按需添加停留、返回原巡逻路线等逻辑

适配修改后的代码

首先给每个路径点挂载独立的路径点属性脚本:

// 挂在场景中每一个路径点对象上
public class Waypoint : MonoBehaviour
{
    // 全局唯一的路径点ID
    public int waypointId;
    // 该路径点直接连通的其他路径点
    public List<Waypoint> connectedWaypoints;
}

修改原有的WaypointController脚本:

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

public class WaypointController : MonoBehaviour
{
    // 原巡逻路径点列表
    public List<Transform> patrolWaypoints = new List<Transform>();
    private Transform targetWaypoint;
    private int patrolTargetIndex = 0;
    private float minDistance = 0.1f;
    private int lastPatrolIndex;

    public bool reversePatrolPath;
    public iTween.EaseType easeType = iTween.EaseType.easeInOutExpo;
    public float moveSpeed = 1.5f;
    public float rotateTime = 0.5f;
    public float iTweenDelay = 0f;

    // 警戒状态相关参数
    private bool isInAlert = false;
    private List<Transform> alertMovePath = new List<Transform>();
    private int alertPathIndex = 0;
    // 全局路径点字典,游戏初始化时把所有场景路径点注册到此处
    public static Dictionary<int, Waypoint> AllWaypoints = new Dictionary<int, Waypoint>();

    void Start()
    {
        lastPatrolIndex = patrolWaypoints.Count - 1;
        targetWaypoint = patrolWaypoints[patrolTargetIndex];
    }

    public void EnemyTurn()
    {
        float distanceToTarget = Vector3.Distance(transform.position, targetWaypoint.position);
        CheckDistanceToWaypoint(distanceToTarget);

        iTween.MoveTo(gameObject, iTween.Hash(
            "position", targetWaypoint.position,
            "delay", iTweenDelay,
            "easetype", easeType,
            "speed", moveSpeed
        ));
    }

    void CheckDistanceToWaypoint(float currentDistance)
    {
        if (currentDistance <= minDistance)
        {
            if (isInAlert)
            {
                alertPathIndex++;
                UpdateAlertTarget();
            }
            else
            {
                patrolTargetIndex++;
                UpdatePatrolTarget();
            }
        }
    }

    // 原巡逻目标更新逻辑
    void UpdatePatrolTarget()
    {
        if (patrolTargetIndex > lastPatrolIndex)
        {
            if (reversePatrolPath)
            {
                patrolWaypoints.Reverse();
            }
            patrolTargetIndex = 1;
        }
        targetWaypoint = patrolWaypoints[patrolTargetIndex];
    }

    // 触发警戒时调用该方法,传入目标路径点的ID
    public void TriggerAlert(int targetWaypointId)
    {
        isInAlert = true;
        alertPathIndex = 0;
        
        Waypoint currentWaypoint = GetCurrentStayWaypoint();
        if (!AllWaypoints.ContainsKey(targetWaypointId) || currentWaypoint == null)
        {
            return;
        }
        Waypoint alertTarget = AllWaypoints[targetWaypointId];
        // BFS计算最短移动路径
        alertMovePath = FindShortestPath(currentWaypoint, alertTarget);
        UpdateAlertTarget();
    }

    // 警戒状态目标更新逻辑
    void UpdateAlertTarget()
    {
        if (alertPathIndex >= alertMovePath.Count)
        {
            // 到达警戒目标点后,此处可自定义后续逻辑,示例为返回巡逻状态
            isInAlert = false;
            patrolTargetIndex = GetNearestPatrolWaypointIndex();
            UpdatePatrolTarget();
            return;
        }
        targetWaypoint = alertMovePath[alertPathIndex];
    }

    // BFS查找最短路径
    List<Transform> FindShortestPath(Waypoint startPoint, Waypoint endPoint)
    {
        Dictionary<Waypoint, Waypoint> parentMap = new Dictionary<Waypoint, Waypoint>();
        Queue<Waypoint> searchQueue = new Queue<Waypoint>();
        HashSet<Waypoint> visitedPoints = new HashSet<Waypoint>();

        searchQueue.Enqueue(startPoint);
        visitedPoints.Add(startPoint);

        while (searchQueue.Count > 0)
        {
            Waypoint current = searchQueue.Dequeue();
            if (current == endPoint)
            {
                break;
            }
            foreach (Waypoint neighbor in current.connectedWaypoints)
            {
                if (!visitedPoints.Contains(neighbor))
                {
                    visitedPoints.Add(neighbor);
                    parentMap[neighbor] = current;
                    searchQueue.Enqueue(neighbor);
                }
            }
        }

        // 回溯生成路径
        List<Transform> resultPath = new List<Transform>();
        Waypoint temp = endPoint;
        while (temp != startPoint)
        {
            resultPath.Add(temp.transform);
            if (!parentMap.ContainsKey(temp))
            {
                break;
            }
            temp = parentMap[temp];
        }
        resultPath.Reverse();
        return resultPath;
    }

    // 获取当前所在的路径点
    Waypoint GetCurrentStayWaypoint()
    {
        foreach (var wp in AllWaypoints.Values)
        {
            if (Vector3.Distance(transform.position, wp.transform.position) < minDistance)
            {
                return wp;
            }
        }
        return null;
    }

    // 警戒结束后找到最近的巡逻路径点继续巡逻
    int GetNearestPatrolWaypointIndex()
    {
        int nearestIndex = 0;
        float minDist = float.MaxValue;
        for (int i = 0; i < patrolWaypoints.Count; i++)
        {
            float dist = Vector3.Distance(transform.position, patrolWaypoints[i].position);
            if (dist < minDist)
            {
                minDist = dist;
                nearestIndex = i;
            }
        }
        return nearestIndex;
    }
}

使用说明

  1. 场景中所有路径点都挂载Waypoint脚本,设置唯一ID,手动选择该点连通的其他路径点
  2. 游戏初始化阶段,把所有路径点按ID为键,存入WaypointController.AllWaypoints全局字典
  3. 投掷石块触发警戒时,调用对应敌人身上WaypointController的TriggerAlert方法,传入目标路径点的ID即可

内容的提问来源于stack exchange,提问作者Nastary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 17:24:05