使用A Pathfinding Project*插件(版本4.2)在多个GridGraph网格自动形成路径

首先创建空物体命名AstarPath ,挂载Pathfinder组件,用来设置网格路径,目前只做了垂直和平面俩个面,实际可以根据需求创建多个面。主要用于在两点之间创建一条最优路线。设置参数:

框红的地方是需要注意的参数设置,包括当前网格的长宽,障碍物设置,可越过的最高坡度等等一些相关设置,会影响路径的计算,具体可以去参考下官方的参数文档。

然后可以找个空的节点或随意节点挂载seeker组件用于寻路的重要组件。

不用管seeker参数设置,默认的就好。

然后创建自定义脚本LineAroundObstacles.cs开始编写代码  这为了实现连线用到了MegaWire插件。

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Vectrosity;
using Pathfinding;

public class LineAroundObstacles : MonoBehaviour
{
    [Header("线缆的材质")]
    public Material wireMaterial;
    [Header("线缆的半径")]
    public float wireSize = 0.1f;
    private bool isDrawing = false; // 绘制状态
    private WireConnectionPoint startPoint; // 线缆的起始连接点

    private Dictionary<MegaWire, List<WireConnectionPoint>> wireConnectionDatas = new Dictionary<MegaWire, List<WireConnectionPoint>>();// 存储当前激活的线缆
    private List<WireConnectionPoint> wirePoints = new List<WireConnectionPoint>();
    private VectorLine currentLine;         // 当前正在拖拽的线条

    private int indexSpanNum = -1;  //记录当前线段下标
    [Header("清空按钮")]
    public Button clearLinesBtn;
    [Header("确认连线按钮")]
    public Button affirmButton;
    [Header("透明按钮")]
    public Button lucencyButton;
    [Header("恢复按钮")]
  public Button recoverButton;
    [Header("返回按钮")]
    public Button returnButton;

    private List<GameObject> tempObjList = new List<GameObject>();
    [Header("启用碰撞效果")]
    public bool isCrash = false;
    [Header("成功音效")]
    public AudioClip succeedClip;
    [Header("失败音效")]
    public AudioClip failClip;

    private AudioSource audioScource;

    private HintPanel hintPanel;

    public Light targetLight;
    public Material targetLightMaterial;

    public Material originalMaterial;
    private Color originalColor;

    private Seeker seeker;
    private Path path;
  void Start()
    {
        seeker = GetComponent<Seeker>();
        //targetLight.gameObject.SetActive(false);
        //targetLightMaterial.DisableKeyword("_EMISSION");
        //if (originalMaterial != null)
        //{
        //    originalColor = originalMaterial.color;
        //}

        wireConnectionDatas = new Dictionary<MegaWire, List<WireConnectionPoint>>(); // 初始化激活的线缆列表
        wirePoints = new List<WireConnectionPoint>(FindObjectsOfType<WireConnectionPoint>());
        //临时存十个空节点
        for (int i = 0; i < 10; i++)
        {
            GameObject tempObj = new GameObject();
            tempObj.name = i.ToString();
            tempObjList.Add(tempObj);
        }
    }

void Update()
    {
        if (Input.GetMouseButtonDown(1))
        {

            // 获取当前鼠标点击的连接点
            WireConnectionPoint clickedPoint = GetClickedConnectionPoint();
            if (clickedPoint != null && !isDrawing)
            {
                startPoint = clickedPoint; // 设置起始点

                // 创建新的 VectorLine
                currentLine = new VectorLine("WireLine", new List<Vector3>(), wireSize, LineType.Continuous, Joins.Weld);
                currentLine.material = wireMaterial;
                // 添加起始点和一个初始的终点
                currentLine.points3.Add(startPoint.transform.position);  // 起始点
                currentLine.points3.Add(startPoint.transform.position);  // 添加一个默认的终点(与起点相同)

                isDrawing = true;
            }
  else if (isDrawing)
            {
                // 如果已经在拖动,鼠标点击添加拐点
                //Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                //if (Physics.Raycast(ray, out RaycastHit hit))
                //{
                Debug.Log("hit=>" + endClickPos);
                currentLine.points3[currentLine.points3.Count - 1] = endClickPos;
                currentLine.points3.Add(endClickPos);
                currentLine.points3.Add(endClickPos);
                //}
            }
        }

        if (isDrawing)
        {
            UpdateWirePosition();
        }
if (Input.GetMouseButtonDown(2))
        {

            WireConnectionPoint endPoint = GetClickedConnectionPoint();
            if (endPoint != null && endPoint != startPoint)
            {
                currentLine.points3[currentLine.points3.Count - 1] = endPoint.transform.position;

                CreateWire(currentLine.points3, endPoint, () =>
                {
                    if (currentLine != null && currentLine.points3.Count > 0)
                    {
                        currentLine.points3.Clear();
                        VectorLine.Destroy(ref currentLine);
                    }
                });
            }
            else
            {
                if (currentLine != null && currentLine.points3.Count > 0)
                {
                    currentLine.points3.Clear();
                    VectorLine.Destroy(ref currentLine);
                }

            }
  isDrawing = false;
        }
        if (Input.GetKeyDown(KeyCode.Z))
        {
            if (indexSpanNum >= 0)
            {
                //从最后一位删除
                MegaWire lastKey = wireConnectionDatas.Keys.Last();
                if (wireConnectionDatas[lastKey] != null)
                {
                    foreach (WireConnectionPoint point in wireConnectionDatas[lastKey])
                    {
                        for (int i = 0; i < point.isConnect.Count; i++)
                        {
                            point.isConnect[i] = false;
                        }
                    }
                }

                DeleteWire(lastKey);
                wireConnectionDatas.Remove(lastKey);
            }


        }
    }
 WireConnectionPoint GetClickedConnectionPoint()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit))
        {
            return hit.collider.GetComponent<WireConnectionPoint>();
        }
        return null;
    }

    Vector3 endClickPos;
    void UpdateWirePosition()
    {

        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit))
        {
            Vector3 mousePos = hit.point;

            Vector3 startPoint = currentLine.points3[currentLine.points3.Count - 2];
             Vector3 constrainedPosition;
            constrainedPosition = mousePos;

            // 更新线条的终点
            if (currentLine != null && currentLine.points3.Count > 0)
            {
                endClickPos = constrainedPosition;
                currentLine.points3[currentLine.points3.Count - 1] = constrainedPosition;
                currentLine.Draw3D();
            }


        }

    }

     //多面路径计算
    List<Vector3> multipathList = new List<Vector3>();
    void CreateWire(List<Vector3> posList, WireConnectionPoint endPoint, UnityAction callback)
    {
        indexSpanNum++;

        HashSet<Vector3> seen = new HashSet<Vector3>();

        posList.RemoveAll(item => !seen.Add(item));
        List<Vector3> changePos = new List<Vector3>(seen);

        while (tempObjList.Count < changePos.Count)
        {
            GameObject temp = new GameObject();
            tempObjList.Add(temp);
        }

        while (tempObjList.Count > changePos.Count)
        {
            Destroy(tempObjList[tempObjList.Count - 1]);
            tempObjList.RemoveAt(tempObjList.Count - 1);
        }



        for (int i = 0; i < changePos.Count; i++)
        {
            tempObjList[i].transform.position = changePos[i];

        }
        MegaWire wire = null;
        // 执行扫描,更新图层
        AstarPath.active.Scan();
        // 获取起点和终点的最近节点
        var startNode = AstarPath.active.GetNearest(startPoint.transform.position);
        var endNode = AstarPath.active.GetNearest(endPoint.transform.position);

        // 输出调试信息,确认起点和终点位置

        Debug.Log("Start Node: " + startNode.node.position+"   "+(startNode.node.Graph== endNode.node.Graph));
        Debug.Log("End Node: " + endNode.node.position);

        if (startNode.node.Graph == endNode.node.Graph)
        {

            seeker.StartPath(startPoint.transform.position, endPoint.transform.position, (p) =>
            {
                if (p.error)
                {
                    Debug.LogError("Pathfinding error: " + p.errorLog);
                    return;
                }

                if (p.vectorPath.Count > 0)
                {
                    Debug.Log("currentNodes==>" + p.vectorPath.Count);  //A*的path不包含头和尾
                    tempObjList = new List<GameObject>();
                    GameObject startObj = new GameObject();
                    startObj.transform.position = startPoint.transform.position;
                    tempObjList.Add(startObj);
                    foreach (Vector3 pos in p.vectorPath)
                    {
                        GameObject obj = new GameObject();
                        obj.transform.position = pos;
                        tempObjList.Add(obj);
                    }
                    GameObject endObj = new GameObject();
                    endObj.transform.position = endPoint.transform.position;
                    tempObjList.Add(endObj);
                }
                wire = MegaWire.Create(wire, tempObjList, wireMaterial, "Wire", null, wireSize, 1.0f);
                //设置物理效果
                wire.Mass = 0.1f;
                wire.stretch = 0.1f;
                wire.massRand = 0.1f;
                wire.doCollisions = isCrash;
                wire.enabled = false;
                //wire.hidespans = false;
                //wire.doCollisions = true;  //贴墙不能设置碰撞,会有问题

                //activeWires.Add(wire);
                if (startPoint.pointName == endPoint.pointName)
                {
                    for (int i = 0; i < startPoint.isConnect.Count; i++)
                    {

                        if (!startPoint.isConnect[i])
                        {
                            startPoint.isConnect[i] = true;
                        }

                    }
                    for (int i = 0; i < endPoint.isConnect.Count; i++)
                    {
                        if (!endPoint.isConnect[i])
                List<WireConnectionPoint> currentPoints = new List<WireConnectionPoint> { startPoint, endPoint };

                    wireConnectionDatas.Add(wire, currentPoints);
                }
                else
                {
                    //多连接错误的线路处理
                    wireConnectionDatas.Add(wire, null);
                }


                //if (activeWires.Count>1)
                //{
                //    activeWires[activeWires.Count - 2].enabled = false;
                //}
                wire.RebuildWire();
                callback?.Invoke();



            });

        }
        else {
            multipathList = new List<Vector3>();
            GridGraph graphStart = startNode.node.Graph as GridGraph;
            GridGraph graphend = endNode.node.Graph as GridGraph;

            // Step 1: 根据终点的位置,计算起点图层的边界点
            Vector3 borderPoint1 = GetGraphBoundaryPoint(graphStart, startPoint.transform.position, endPoint.transform.position);
            CalculatePath(startPoint.transform.position, borderPoint1, 0,endPoint,wire,callback);  // 使用第一个图层

            // Step 2: 根据起点的位置,计算终点图层的边界点
            Vector3 borderPoint2 = GetGraphBoundaryPoint(graphend, endPoint.transform.position, startPoint.transform.position);
            CalculatePath(borderPoint1, borderPoint2, 1, endPoint, wire, callback);  // 使用第二个图层

            // Step 3: 计算从第二个图层的边界点到终点的路径
            CalculatePath(borderPoint2, endPoint.transform.position, 1, endPoint, wire, callback, true);  // 使用第二个图层

        }

    }
 // 计算路径
    void CalculatePath(Vector3 start, Vector3 end, int graphIndex,WireConnectionPoint endPoint, MegaWire wire,UnityAction callback, bool calculatePath=false)
    {
        if (AstarPath.active != null && AstarPath.active.graphs.Length > graphIndex)
        {
            GridGraph targetGraph = AstarPath.active.graphs[graphIndex] as GridGraph;
            if (targetGraph != null)
            {
                // 设置 Seeker 使用指定的图层来计算路径
                seeker.StartPath(start, end, (p)=> {
                    if (p.error)
                    {
                        Debug.LogError("路径计算错误: " + p.errorLog);
                        return;
                    }

                    // 处理路径,比如让对象沿着路径移动等
                    Debug.Log("路径计算成功!");
                    multipathList.AddRange(p.vectorPath);
                    Debug.Log("currentNodes==>" + p.vectorPath.Count);  //A*的path不包含头和尾
                        tempObjList = new List<GameObject>();
                        GameObject startObj = new GameObject();
                        startObj.transform.position = startPoint.transform.position;
                        tempObjList.Add(startObj);
                        foreach (Vector3 pos in p.vectorPath)
                        {
                            GameObject obj = new GameObject();
                            obj.transform.position = pos;
                            tempObjList.Add(obj);
                        }
                        GameObject endObj = new GameObject();
                        endObj.transform.position = endPoint.transform.position;
                        tempObjList.Add(endObj);
                    }


                    wire = MegaWire.Create(wire, tempObjList, wireMaterial, "Wire", null, wireSize, 1.0f);
                    //设置物理效果
              wire.Mass = 0.1f;
                    wire.stretch = 0.1f;
                    wire.massRand = 0.1f;
                    wire.doCollisions = isCrash;
                    wire.enabled = false;
                    //wire.hidespans = false;
                    //wire.doCollisions = true;  //贴墙不能设置碰撞,会有问题

                    //activeWires.Add(wire);
                    if (startPoint.pointName == endPoint.pointName)
                    {
                        for (int i = 0; i < startPoint.isConnect.Count; i++)
                        {

                            if (!startPoint.isConnect[i])
                            {
                                startPoint.isConnect[i] = true;
                            }

                        }
                        for (int i = 0; i < endPoint.isConnect.Count; i++)
                        {
                            if (!endPoint.isConnect[i])
                            {
                                endPoint.isConnect[i] = true;
                            }
                        }

                        List<WireConnectionPoint> currentPoints = new List<WireConnectionPoint> { startPoint, endPoint };

                        wireConnectionDatas.Add(wire, currentPoints);
                    }
                    else
                    {
                        //多连接错误的线路处理
                        wireConnectionDatas.Add(wire, null);
                    }

                    
                    //if (activeWires.Count>1)
                    //{
                    //    activeWires[activeWires.Count - 2].enabled = false;
                    //}
                    wire.RebuildWire();
                    callback?.Invoke();

                });

            
            }
        }
    }
// 根据终点的位置和起点的位置,计算图层的边界点
    Vector3 GetGraphBoundaryPoint(GridGraph graph, Vector3 point, Vector3 oppositePoint)
    {
        // 获取终点或起点在图层的对应边界点
        // 我们将选择离另一点最近的边界点作为连接点
        Vector3 boundaryPoint = point;
        float minDist = float.MaxValue;

        // 计算图层的边界点
        for (int x = 0; x < graph.width; x++)
        {
            for (int z = 0; z < graph.depth; z++)
            {
                // 获取每个网格节点的位置
                Vector3 nodePosition = (Vector3)graph.GetNode(x, z).position;
               
                // 计算与对方点之间的距离
                float dist = Vector3.Distance(oppositePoint, nodePosition);

                // 选择最接近对方点的图层边界点
                if (dist < minDist)
                {
                    minDist = dist;
                    boundaryPoint = nodePosition;
                }
            }
        }
        Debug.Log("boundaryPoint==>"+ boundaryPoint);
        return boundaryPoint;
    }

 

    public void DeleteWire(MegaWire wireToDelete)
    {
        if (wireConnectionDatas.ContainsKey(wireToDelete))
        {
            indexSpanNum--;
            wireToDelete.RemoveWire(); // 删除线缆
            if (wireConnectionDatas[wireToDelete] != null)
            {
                foreach (WireConnectionPoint point in wireConnectionDatas[wireToDelete])
                {
                    for (int i = 0; i < point.isConnect.Count; i++)
                    {
                        point.isConnect[i] = false;
                    }
                }
            }

            wireConnectionDatas.Remove(wireToDelete);
        }
    }
 //是否完成连线
    private bool ConfirmConnection()
    {

        foreach (var point in wirePoints)
        {
            foreach (var item in point.isConnect)
            {
                if (!item)
                {
                    return false;
                }
            }
        }
        foreach (var item in wireConnectionDatas.Values)
        {
            if (item == null)
            {
                return false;
            }
        }

        return true;
    }
 // 将材质变为半透明
    public void SetTransparent(float alpha)
    {
        if (originalMaterial != null)
        {
            // 设置渲染模式为透明
            originalMaterial.SetFloat("_Mode", 3);
            originalMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
            originalMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
            originalMaterial.SetInt("_ZWrite", 0);
            originalMaterial.DisableKeyword("_ALPHATEST_ON");
            originalMaterial.EnableKeyword("_ALPHABLEND_ON");
            originalMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
            originalMaterial.renderQueue = 3000;

            // 设置透明度
            Color color = originalMaterial.color;
            color.a = alpha;
            originalMaterial.color = color;
        }
    }
 // 恢复材质为不透明
    public void SetOpaque()
    {
        if (originalMaterial != null)
        {
            // 恢复渲染模式为不透明
            originalMaterial.SetFloat("_Mode", 0);
            originalMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
            originalMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
            originalMaterial.SetInt("_ZWrite", 1);
            originalMaterial.DisableKeyword("_ALPHATEST_ON");
            originalMaterial.DisableKeyword("_ALPHABLEND_ON");
            originalMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
            originalMaterial.renderQueue = -1;

            // 恢复材质的原始颜色
            originalMaterial.color = originalColor;
        }
    }
private void OnDestroy()
    {
        tempObjList.Clear();
    }
}

代码里有很多冗余,如果只想了解获取路径只看CreateWire方法就好了。

有什么不理解的地方欢迎留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值