NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/Map/MapPathfind.cs

940 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Collections.Generic;
using System.IO;
using TGS;
using Unity.Mathematics;
using UnityEngine;
using System.Diagnostics;
public class MapHex
{
public const float Length = 1.73205f;
public const float Radius = 1.5f;
public int index;
public int Nx;
public int Nz;
public int Ax;
public int Ay;
public int Az;
public float Wx;
public float Wz;
public int CityID;
public void SetData( int nWidth, int nHeight, int nIndex )
{
//index = Nz * nWidth + Nx
index = nIndex;
Nz = nIndex/nWidth;
Nx = nIndex - Nz *nWidth;
Az = Nz;
Ax = Nx-Nz/2;
Ay = -Ax-Az;
Wx = (Nx + Nz * 0.5f - Nz / 2 ) * (Radius*2.0f);
Wz = Nz * (Length*1.5f);
}
public override string ToString() {
return $"index={index}, NC=({Nx}, {Nz}), AC=({Ax}, {Ay}, {Az}), WC=({Wx}, 0, {Wz}), City ID={CityID}";
}
public bool IsMyNext(MapHex hex)
{
int xOffset = math.abs(hex.Ax- Ax);
int zOffset = math.abs(hex.Az-Az);
int yOffset = math.abs(hex.Ay-Ay);
return xOffset <= 1 && zOffset <= 1 && yOffset <= 1;
}
public bool IsMe(MapHex hex)
{
return hex.Nx == Nx && hex.Nz == Nz;
}
public int Distance(MapHex hex)
{
int xOffset = math.abs(hex.Ax- Ax);
int zOffset = math.abs(hex.Az-Az);
int yOffset = math.abs(hex.Ay-Ay);
return math.max(yOffset, math.max(xOffset, zOffset));
}
public bool CanPass(int startCityID, int endCityID)
{
if(CityID > 0) {
return CityID == startCityID || CityID == endCityID;
} else {
return true;
}
}
}
public class PathNode
{
public MapHex hex;
public HexDir dir;
//public List<PathNode> childs;
public PathNode parent;
public CityRoad cr;
public int crIndex;
public bool goRight;
public PathNode()
{
//childs = new List<PathNode>();
}
}
public class CityRoad
{
public MapHex [] road;
public void GetNextHex(MapHex hex, out MapHex left, out int leftIndex, out MapHex right, out int rightIndex)
{
int index = -1;
for(int i = 0; i < road.Length; i ++)
{
if(road[i].IsMe(hex))
{
index = i;
break;
}
}
if(index == -1)
{
UnityEngine.Debug.LogError($"CityRoad GetNextHex not found index, hex={hex}");
left = null;
right = null;
leftIndex = 0;
rightIndex = 0;
return;
}
leftIndex = index -1;
if(leftIndex < 0)
leftIndex = road.Length - 1;
rightIndex = index +1;
if(rightIndex >= road.Length)
rightIndex = 0;
left = road[leftIndex];
right = road[rightIndex];
//UnityEngine.Debug.Log($"GetNextHex 1 rsult, index={index}, leftIndex={leftIndex}, rightIndex={rightIndex}");
//UnityEngine.Debug.Log($"hex={hex}");
//UnityEngine.Debug.Log($"left={left}");
//UnityEngine.Debug.Log($"right={right}");
}
public void GetNextHex(int index, bool goRight, out MapHex next, out int nextIndex)
{
if(goRight)
{
index ++;
if(index >= road.Length)
index = 0;
}
else
{
index --;
if(index < 0)
index = road.Length - 1;
}
nextIndex = index;
next = road[index];
//UnityEngine.Debug.Log($"GetNextHex 2 index={index}, goRight={goRight}, nextIndex={nextIndex}");
//UnityEngine.Debug.Log($"next={next}");
}
}
public enum HexDir
{
None = 0,
RightUp,
RightDown,
LeftUp,
LeftDown,
Right,
Left,
UpRL,
DownRL,
}
[ExecuteAlways]
public class MapPathfind : MonoBehaviour
{
public TerrainGridSystem TGS;
MapData m_mapData;
public bool Reload;
public MapHex [] m_hexes;
// width x轴
// height z轴
public int startNx;
public int startNz;
public int endNx;
public int endNz;
public bool FindPath;
MapHex m_Start;
MapHex m_End;
Dictionary<int, CityRoad> m_Roads;
public GameObject PathGo;
public GameObject RoundGo;
public GameObject CityGo;
public GameObject StartGo;
public GameObject EndGo;
PathNode m_EndNode;
public HashSet<int> m_CloseList;
public bool DestroyPath;
void Start()
{
Reload = true;
// 19,3 0,15
}
void CreateMap()
{
m_Roads = new Dictionary<int, CityRoad>();
string jsonPath = Application.dataPath + "/Editor/MapPath/MapData_MapsTest.json";
string txt = File.ReadAllText(jsonPath);
m_mapData = JsonUtility.FromJson<MapData>(txt);
UnityEngine.Debug.Log($"width={m_mapData.Width}, height={m_mapData.Height}, data length={m_mapData.BlocksData.Count}");
m_hexes = new MapHex[m_mapData.BlocksData.Count];
for(int i = 0; i < m_mapData.BlocksData.Count; i ++)
{
MapHex hex = new MapHex();
hex.SetData(m_mapData.Width, m_mapData.Height, i);
m_hexes[i] = hex;
//UnityEngine.Debug.Log(hex.ToString());
}
AddCity(1, 10, 21, 1 );
AddCity(2, 12, 21, 1 );
AddCity(3, 4, 19, 1 );
AddCity(4, 16, 19, 1 );
AddCity(5, 14, 18, 1 );
AddCity(6, 12, 17, 1 );
AddCity(7, 4, 16, 1 );
AddCity(8, 2, 15, 1 );
AddCity(9, 14, 13, 1 );
AddCity(10, 6, 12, 1 );
AddCity(11, 15, 11, 1 );
AddCity(12, 9, 10, 1 );
AddCity(13, 9, 6, 1 );
AddCity(14, 6, 21, 2 );
AddCity(15, 16, 16, 2 );
AddCity(16, 3, 11, 2 );
AddCity(17, 13, 8, 2 );
AddCity(18, 9, 15, 3 );
}
public void AddCity(int nCityID, int Nx, int Nz, int range )
{
MapHex center = m_hexes[GetIndex(Nx, Nz)];
// 找最外一圈路径
List<MapHex> RoundRoad = new List<MapHex>();
// 获取建筑所在的格子
int MaxX = center.Ax + range;
int MinX = center.Ax - range;
int MaxY = center.Ay + range;
int MinY = center.Ay - range;
int MaxZ = center.Az + range;
int MinZ = center.Az - range;
for(int x = MinX; x <= MaxX; x ++)
{
for(int y = MinY; y <= MaxY; y ++)
{
int z = -x-y;
if(z >= MinZ && z <= MaxZ)
{
MapHex hex = GetMapHex(x, y, z);
if(x == MinX || x == MaxX
|| y == MinY || y == MaxY
|| z == MinZ || z == MaxZ)
{
RoundRoad.Add(hex);
}
else
{
hex.CityID = nCityID;
//UnityEngine.Debug.Log(hex.ToString());
/*
var go = Instantiate(CityGo);
go.transform.SetParent(transform);
go.transform.position = new Vector3(hex.Wx, 0, hex.Wz);
*/
}
}
}
}
int RoadNodeCount = RoundRoad.Count;
List<MapHex> rounds = new List<MapHex>();
MapHex next = RoundRoad[0];
rounds.Add(next);
RoundRoad.RemoveAt(0);
while(RoundRoad.Count > 0)
{
MapHex nextHex = null;
for(int i = 0; i < RoundRoad.Count; i ++)
{
if(next.IsMyNext(RoundRoad[i]))
{
next = RoundRoad[i];
RoundRoad.RemoveAt(i);
rounds.Add(next);
nextHex = next;
break;
}
}
if(nextHex == null)
{
UnityEngine.Debug.LogError("nextHex not found ...");
break;
}
}
for(int i = 0; i < rounds.Count; i ++)
{
UnityEngine.Debug.Log($"find next, city={nCityID}, count={rounds.Count}, current={rounds[i]}");
}
if(rounds.Count == RoadNodeCount)
{
CityRoad cr = new CityRoad();
cr.road = rounds.ToArray();
m_Roads.Add(nCityID, cr);
/*
for(int i = 0; i < cr.road.Length; i ++)
{
var go = Instantiate(RoundGo);
go.transform.SetParent(transform);
go.transform.position = new Vector3(cr.road[i].Wx, 0, cr.road[i].Wz);
}
//*/
}
else
{
UnityEngine.Debug.LogError($"Road create error, city={nCityID}, rounds.Count:{rounds.Count} != RoadNodeCount:{RoadNodeCount}");
}
/*
UnityEngine.Debug.Log("Road Start ========================= count=" +rounds.Count);
for(int i = 0; i < rounds.Count; i ++)
{
UnityEngine.Debug.Log(rounds[i]);
}
UnityEngine.Debug.Log("Road End =========================");
*/
}
int GetIndex(int Nx, int Nz)
{
return Nz*m_mapData.Width+Nx;
}
void Update()
{
if(Reload)
{
Reload = false;
CreateMap();
}
if(FindPath)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
DoFind2();
stopwatch.Stop();
FindPath = false;
UnityEngine.Debug.Log($"DoFind2 执行时间: {stopwatch.ElapsedMilliseconds} 毫秒");
}
if(DestroyPath)
{
DestroyPath = false;
//删除本节点下所有的GameObect
foreach (Transform child in transform)
{
DestroyImmediate(child.gameObject);
}
}
}
HexDir GetTargetPositionDir(MapHex start, MapHex end)
{
var dir = GetLineDir(start, end);
if(dir != HexDir.None)
return dir;
if(end.Az > start.Az)
{
if(end.Ax < start.Ax && end.Ay < start.Ay)
{
return HexDir.UpRL;
}
else if(end.Ay > start.Ay)
{
return HexDir.LeftUp;
}
else
{
return HexDir.RightUp;
}
}
else
{
if(end.Ax > start.Ax && end.Ay > start.Ay)
{
return HexDir.DownRL;
}
else if(end.Ax < start.Ax)
{
return HexDir.LeftDown;
}
else
{
return HexDir.RightDown;
}
}
}
HexDir GetLineDir(MapHex start, MapHex end)
{
if(start.Ax == end.Ax)
{
if(end.Az > start.Az)
{
return HexDir.RightUp;
}
else
{
return HexDir.LeftDown;
}
}
else if( start.Az == end.Az)
{
if(end.Ax > start.Ax)
{
return HexDir.Right;
}
else
{
return HexDir.Left;
}
}
else if( start.Ay == end.Ay)
{
if(end.Az > start.Az)
{
return HexDir.LeftUp;
}
else
{
return HexDir.RightDown;
}
}
return HexDir.None;
}
void AddPathNode(PathNode node, PathNode parent)
{
node.parent = parent;
}
bool GetNextNode(PathNode currentNode, List<PathNode> openList, int startCityID, int endCityID)
{
var dir = currentNode.dir;
var nextHex = GetNextHex(currentNode.hex, dir);
if(nextHex != null)
{
// 下一格是建筑,那当前这一格,肯定就在路点上
if(!nextHex.CanPass(startCityID, endCityID))
{
// 如果当前格没有路点信息,则找左右两个路点作为下一格
if(currentNode.cr == null)
{
m_Roads.TryGetValue(nextHex.CityID, out CityRoad cr);
if(cr == null)
{
UnityEngine.Debug.LogError("City road not found, city id=" + nextHex.CityID);
return false;
}
cr.GetNextHex(currentNode.hex, out MapHex left, out int leftIndex, out MapHex right, out int rightIndex);
if(!m_CloseList.TryGetValue(left.index, out int hexIndex))
{
PathNode nodeLeft = new PathNode();
nodeLeft.hex = left;
nodeLeft.dir = GetTargetPositionDir(left, m_End);
nodeLeft.cr = cr;
nodeLeft.crIndex = leftIndex;
nodeLeft.goRight = false;
//nodeLeft.parent = currentNode;
AddPathNode(nodeLeft, currentNode);
if(m_End.IsMe(left))
{
m_EndNode = nodeLeft;
return true;
}
openList.Add(nodeLeft);
}
if(!m_CloseList.TryGetValue(right.index, out int hexIndex2))
{
PathNode nodeRight = new PathNode();
nodeRight.hex = right;
nodeRight.dir = GetTargetPositionDir(right, m_End);
nodeRight.cr = cr;
nodeRight.crIndex = rightIndex;
nodeRight.goRight = true;
//nodeRight.parent = currentNode;
AddPathNode(nodeRight, currentNode);
if(m_End.IsMe(right))
{
m_EndNode = nodeRight;
return true;
}
openList.Add(nodeRight);
}
}
else // 当前格本身就在路点上,则找下一格路点
{
currentNode.cr.GetNextHex(currentNode.crIndex, currentNode.goRight, out MapHex next, out int nextIndex);
if(!m_CloseList.TryGetValue(next.index, out int hexIndex2))
{
PathNode nodeNext = new PathNode();
nodeNext.hex = next;
nodeNext.cr = currentNode.cr;
nodeNext.dir = GetTargetPositionDir(next, m_End);
nodeNext.goRight = currentNode.goRight;
nodeNext.crIndex = nextIndex;
//nodeNext.parent = currentNode;
AddPathNode(nodeNext, currentNode);
if(m_End.IsMe(next))
{
m_EndNode = nodeNext;
return true;
}
openList.Add(nodeNext);
}
}
}
else
{
PathNode nextNode = new PathNode();
nextNode.hex = nextHex;
nextNode.dir = dir;
var dirNext = GetLineDir(nextHex, m_End);
if(dirNext != HexDir.None)
{
nextNode.dir = dirNext;
}
nextNode.parent = currentNode;
AddPathNode(nextNode, currentNode);
if(m_End.IsMe(nextHex))
{
m_EndNode = nextNode;
return true;
}
openList.Add(nextNode);
}
}
return false;
}
void DoFind2()
{
m_EndNode = null;
m_CloseList = new HashSet<int>();
List<MapHex> m_paths = new List<MapHex>();
m_Start = m_hexes[GetIndex(startNx, startNz)];
var StartCityID = m_Start.CityID;
m_End = m_hexes[GetIndex(endNx, endNz)];
var EndCityID = m_End.CityID;
StartGo.transform.position = new Vector3(m_Start.Wx, 0, m_Start.Wz);
EndGo.transform.position = new Vector3(m_End.Wx, 0, m_End.Wz);
List<PathNode> openList = new List<PathNode>();
PathNode path = new PathNode();
path.hex = m_Start;
path.dir = GetTargetPositionDir(m_Start, m_End);
openList.Add(path);
List<MapHex> hexList = new List<MapHex>();
while(openList.Count > 0)
{
//UnityEngine.Debug.Log("START ============================================== openList.Count=" + openList.Count);
List<PathNode> newOpenList = new List<PathNode>();
for(int i = 0; i < openList.Count; i ++)
{
hexList.Clear();
var node = openList[i];
m_CloseList.Add(node.hex.index);
//UnityEngine.Debug.Log($"node.dir={node.dir}, node={node.hex}");
switch(node.dir)
{
case HexDir.DownRL:
{
node.dir = HexDir.LeftDown;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("LeftDown go end");
goto End;
}
node.dir = HexDir.RightDown;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("RightDown go end");
goto End;
}
}
break;
case HexDir.UpRL:
{
node.dir = HexDir.LeftUp;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("LeftUp go end");
goto End;
}
node.dir = HexDir.RightUp;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("RightUp go end");
goto End;
}
}
break;
//*
case HexDir.LeftUp:
{
node.dir = HexDir.Left;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("LeftUp HexDir.Left go end");
goto End;
}
node.dir = HexDir.LeftUp;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("HexDir.LeftUp go end");
goto End;
}
}
break;
case HexDir.LeftDown:
{
node.dir = HexDir.Left;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("LeftDown HexDir.Left go end");
goto End;
}
node.dir = HexDir.LeftDown;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("HexDir.LeftDown go end");
goto End;
}
}
break;
case HexDir.RightUp:
{
node.dir = HexDir.Right;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("RightUp HexDir.Right go end");
goto End;
}
node.dir = HexDir.RightUp;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("HexDir.RightUp go end");
goto End;
}
}
break;
case HexDir.RightDown:
{
node.dir = HexDir.Right;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("RightDown HexDir.Right go end");
goto End;
}
node.dir = HexDir.RightDown;
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("HexDir.RightDown go end");
goto End;
}
}
break;
//*/
default:
{
if(GetNextNode(node, newOpenList, StartCityID, EndCityID))
{
UnityEngine.Debug.Log("default go end");
goto End;
}
}
break;
}
}
openList.Clear();
openList.AddRange(newOpenList);
//UnityEngine.Debug.Log("NEXT ============================================== openList.Count=" + openList.Count);
}
End:
/*
PathNode pathNode2 = m_EndNode;
while(pathNode2.parent != null)
{
PathNode check = pathNode2.parent;
while(check.parent != null)
{
var dir = GetLineDir(pathNode2.hex, check.hex);
if(dir != HexDir.None)
{
List<MapHex> list = new List<MapHex>();
MapHex hex = pathNode2.hex;
while(true)
{
MapHex nextHex = GetNextHex(hex, dir);
if(nextHex.CityID <= 0)
{
if(nextHex.IsMe(check.hex))
{
break;
}
list.Add(nextHex);
hex = nextHex;
}
else
{
list.Clear();
break;
}
}
if(list.Count > 0)
{
PathNode newAdd = pathNode2;
while(list.Count > 0)
{
PathNode newNext = new PathNode();
newNext.hex = list[0];
newAdd.parent = newNext;
newAdd = newNext;
list.RemoveAt(0);
}
newAdd.parent = check;
}
}
check = check.parent;
}
pathNode2 = pathNode2.parent;
}
//*/
int nCount = 0;
PathNode pathNode = m_EndNode;
while(pathNode.parent != null)
{
UnityEngine.Debug.Log(pathNode.hex);
var go = Instantiate(PathGo);
go.transform.SetParent(transform);
go.transform.position = new Vector3(pathNode.hex.Wx, 0, pathNode.hex.Wz);
nCount ++;
pathNode = pathNode.parent;
}
UnityEngine.Debug.Log("all path node count="+ nCount);
}
MapHex GetMapHex(int Ax, int Ay, int Az)
{
int Nz = Az;
int Nx = Ax+Az/2;
//UnityEngine.Debug.Log($"GetMapHex Ax={Ax}, Ay={Ay}, Az={Az}, Nx={Nx}, Nz={Nz}");
if(Nx < 0 || Nx > m_mapData.Width-1)
{
//UnityEngine.Debug.LogError($"GetMapHex 1 not found Nx={Nx}, Nz={Nz}");
return null;
}
if(Nz < 0 || Nz > m_mapData.Height-1)
{
//UnityEngine.Debug.LogError($"GetMapHex 2 not found Nx={Nx}, Nz={Nz}");
return null;
}
int index = GetIndex(Nx, Nz);
return m_hexes[index];
}
/*
*/
MapHex GetNextHex(MapHex hex, HexDir dir)
{
switch(dir)
{
case HexDir.RightUp:
{
//往右上走Ax不变Az+1
int NextAx = hex.Ax;
int NextAz = hex.Az+1;
int NextAy = -NextAx-NextAz;
return GetMapHex(NextAx, NextAy, NextAz);
}
case HexDir.RightDown:
{
// 往右下走Ay不变Az-1
int NextAz = hex.Az-1;
int NextAy = hex.Ay;
int NextAx = -NextAz-NextAy;
return GetMapHex(NextAx, NextAy, NextAz);
}
case HexDir.LeftUp:
{
//往左上走Ay不变Az+1
int NextAy = hex.Ay;
int NextAz = hex.Az+1;
int NextAx = -NextAz-NextAy;
return GetMapHex(NextAx, NextAy, NextAz);
}
case HexDir.LeftDown:
{
//往左下走Ax不变Az-1
int NextAx = hex.Ax;
int NextAz = hex.Az-1;
int NextAy = -NextAx-NextAz;
return GetMapHex(NextAx, NextAy, NextAz);
}
case HexDir.Right:
{
//往右走Az不变Ay-1
int NextAz = hex.Az;
int NextAy = hex.Ay-1;
int NextAx = -NextAz-NextAy;
return GetMapHex(NextAx, NextAy, NextAz);
}
case HexDir.Left:
{
//往左走Az不变Ay+1
int NextAz = hex.Az;
int NextAy = hex.Ay+1;
int NextAx = -NextAz-NextAy;
return GetMapHex(NextAx, NextAy, NextAz);
}
default:
UnityEngine.Debug.LogError("GetNextHex invalid dir, dir=" + dir);
return null;
}
}
}