using System.Collections; using System.Collections.Generic; using UnityEngine; using Newtonsoft.Json; /** 狼的行动区域,为了避免狼的行动目的坐标重合的问题。 */ public class WolfActGrid : MonoBehaviour { [SerializeField] TextAsset jsonText; JsonConfig config; [System.NonSerialized] public AreaMatrix areaMatrix = new AreaMatrix(); [SerializeField] Material[] sphereMaterials; public static WolfActGrid ins; //调试绘制模式-手动开关 bool debugDrawing = false; //调试-生成新的地域矩阵,不从配置中读取 bool debugNewAreaMatrix = false; void Awake() { ins = this; areaMatrix.wolfActGrid = this; config = JsonConfig.Load(jsonText.text); if (debugNewAreaMatrix) { createAreaMatrix(); } else { loadAreaMatrix(); } } //从配置中加载地域矩阵(前提是有保存在json配置文件内) void loadAreaMatrix() { List> posMatrix = areaMatrix.posMatrix = config.GetPosMatrix(); config.SetPosMatrixNull(); for (int r = 0; r < posMatrix.Count; r++) { for (int c = 0; c < posMatrix[r].Count; c++) { Vector3 pos = posMatrix[r][c]; DebugDrawSphere(pos, r, c); } } } //动态创建地域矩阵 void createAreaMatrix() { float circleRadius = 0.6f; Vector3 standardPosition = transform.position; Vector3 standardPointer = transform.forward; float minAngleY = -14f; float maxAngleY = 16f; for (float distance = 11.5f; distance <= 50f; distance += circleRadius * 2) { List posList = new List(); areaMatrix.posMatrix.Add(posList); float sinValue = circleRadius / distance; float deltaAngle = 2f * Mathf.Asin(sinValue) / Mathf.PI * 180f; for (int col = 0; ; col++) { float angleY = minAngleY + deltaAngle * col; if (angleY > maxAngleY) break; Vector3 pointer = Quaternion.AngleAxis(angleY, Vector3.up) * standardPointer; Vector3 pointerWithLen = pointer * distance; Vector3 newPos = standardPosition + pointerWithLen; #region 射线检测是否被树遮挡 float checkRayDistance = 60f; Vector3 rayPos = newPos; Vector3 rayDir = Quaternion.AngleAxis(180, Vector3.up) * pointer; if (Physics.Raycast(rayPos, rayDir, checkRayDistance, LayerMask.GetMask("Test"))) { continue; } Vector3 pointerWithLen1 = Quaternion.AngleAxis(-deltaAngle / 3f, Vector3.up) * pointerWithLen; Vector3 rayPos1 = standardPosition + pointerWithLen1; Vector3 rayDir1 = Quaternion.AngleAxis(180, Vector3.up) * pointerWithLen1; if (Physics.Raycast(rayPos1, rayDir1, checkRayDistance, LayerMask.GetMask("Test"))) { continue; } Vector3 pointerWithLen2 = Quaternion.AngleAxis(deltaAngle / 3f, Vector3.up) * pointerWithLen; Vector3 rayPos2 = standardPosition + pointerWithLen2; Vector3 rayDir2 = Quaternion.AngleAxis(180, Vector3.up) * pointerWithLen2; if (Physics.Raycast(rayPos2, rayDir2, checkRayDistance, LayerMask.GetMask("Test"))) { continue; } #endregion posList.Add(newPos); DebugDrawSphere(newPos, areaMatrix.posMatrix.Count - 1, posList.Count - 1); } } } void DebugDrawSphere(Vector3 pos, int r, int c) { if (!debugDrawing) return; GameObject sphere = transform.Find("Sphere").gameObject; GameObject o = GameObject.Instantiate(sphere, pos, Quaternion.identity, transform); o.name = r + "_" + c; o.SetActive(true); o.AddComponent().pos = pos; } public void DebugOccupySphere(string name, bool occupy) { if (!debugDrawing) return; transform.Find(name).GetComponent().material = occupy ? sphereMaterials[1] : sphereMaterials[0]; } //提供给狼的接口,扑击结束后,跑回的地点 int runBackPointIndex = -1; public Vector3 GetRunBackPointAfterPounce() { if (runBackPointIndex == -1) runBackPointIndex = Random.Range(0, 2); runBackPointIndex++; return this.transform.Find("RunBackPoint" + (runBackPointIndex % 2)).position; } public class AreaMatrix { public List> posMatrix = new List>(); //格式:"行_列" public Dictionary occupyDC = new Dictionary(); public WolfActGrid wolfActGrid; //计算水平距离 float calDis(Vector3 p1, Vector3 p2) { return Mathf.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.z - p2.z) * (p1.z - p2.z)); } // 占用距离最近的格子,如果最近的被占用则广度优先寻找附近较近的格子 public bool occupyPos(Vector3 p, object obj) { float minDis = float.MaxValue; int bestR = 0; int bestC = 0; for (int r = 0; r < posMatrix.Count; r++) { for (int c = 0; c < posMatrix[r].Count; c++) { Vector3 pos = posMatrix[r][c]; float dis = calDis(pos, p); if (dis < minDis) { minDis = dis; bestR = r; bestC = c; } } } object cur_occupy_obj = checkOccupy(bestR, bestC); //如果目标格子被其它对象占用,则过渡优先寻找附近的格子,因为矩阵是扇形,目前的fbs有偏差,但无妨 if (cur_occupy_obj != null && !obj.Equals(cur_occupy_obj)) { int sr = bestR; int sc = bestC; List openList = new List(); openList.Add(sr + "_" + sc); int scanIndex = 1; bool breakBFS = false; while (true) { int i = 0; while (i < 8) { i++; if (i == 1) { sc++; } if (i == 2) { sr--; } if (i == 3) { sc--; } if (i == 4) { sc--; } if (i == 5) { sr++; } if (i == 6) { sr++; } if (i == 7) { sc++; } if (i == 8) { sc++; } if (sr >= 0 && sr < posMatrix.Count && sc >= 0 && sc < posMatrix[sr].Count) { string keyName = sr + "_" + sc; if (!openList.Contains(keyName)) { object now_occupy_obj = checkOccupy(sr, sc); if (now_occupy_obj == null || obj.Equals(now_occupy_obj)) { bestR = sr; bestC = sc; breakBFS = true; break; } //加入 openList.Add(keyName); } } } if (breakBFS) break; if (scanIndex < openList.Count) { string[] str = openList[scanIndex].Split('_'); scanIndex++; sr = int.Parse(str[0]); sc = int.Parse(str[1]); } else { return false; } } } //找到最佳的格子 Vector3 bestPoint = posMatrix[bestR][bestC]; p.x = bestPoint.x; p.z = bestPoint.z; //释放自己以前占用的格子 releaseOccupy(obj); //占用目标格子 Occupy(bestR, bestC, obj); return true; } public object checkOccupy(int r, int c) { string keyName = r + "_" + c; foreach (var item in occupyDC) { if (item.Value.Equals(keyName)) return item.Key; } return null; } public void releaseOccupy(object obj) { string keyName = null; occupyDC.TryGetValue(obj, out keyName); if (keyName == null) { return; } occupyDC.Remove(obj); wolfActGrid.DebugOccupySphere(keyName, false); } public void Occupy(int r, int c, object obj) { string keyName = r + "_" + c; occupyDC[obj] = keyName; wolfActGrid.DebugOccupySphere(keyName, true); if (!wolfActGrid.debugDrawing) return; if (obj.GetType().Equals(typeof(Wolf))) { Wolf wolf = (Wolf) obj; WolfLineRender wolfLineRender = wolf.GetComponentInChildren(); if (!wolfLineRender) { GameObject prefab = wolfActGrid.transform.Find("WolfLineRender").gameObject; GameObject o = GameObject.Instantiate(prefab, wolf.transform.position + Vector3.up * 0.8f, Quaternion.identity, wolf.transform); wolfLineRender = o.GetComponent(); o.SetActive(true); } wolfLineRender.SetDestination(posMatrix[r][c]); } } } class JsonConfig { public string posMatrix = null; //清空,避免占用内存 public void SetPosMatrixNull() { posMatrix = null; } public List> GetPosMatrix() { string[] lines = posMatrix.Split('\n'); List> list = new List>(); foreach (var line in lines) { if (line.Trim().Length == 0) continue; string[] vectorStrs = line.Split(' '); List childList = new List(); foreach (var vectorStr in vectorStrs) { string[] valStrs = vectorStr.Split('_'); Vector3 vec = new Vector3( float.Parse(valStrs[0]), float.Parse(valStrs[1]), float.Parse(valStrs[2]) ); childList.Add(vec); } list.Add(childList); } return list; } public void SetPosMatrix(List> list) { posMatrix = ""; for (int i = 0; i < list.Count; i++) { for (int j = 0; j < list[i].Count; j++) { Vector3 pos = list[i][j]; posMatrix += pos.x + "_" + pos.y + "_" + pos.z; if (j < list[i].Count - 1) posMatrix += " "; } if (i < list.Count - 1) posMatrix += "\n"; }; } public void Write() { string filePath = "E:\\UnityProject\\SmartBow\\Assets\\BowArrow\\Scripts\\GameChallenge\\WoldActGird\\WolfActGrid.json"; if (!System.IO.File.Exists(filePath)) { Debug.LogError("JsonConfig-File-NotExist"); return; } try { System.IO.StreamWriter writer = new System.IO.StreamWriter(filePath); writer.Write(JsonConvert.SerializeObject(this)); writer.Close(); } catch (System.Exception e) { Debug.LogError("JsonConfig-Write-Fail"); Debug.LogError(e.Message); } } public static JsonConfig Load(string jsonText) { JsonConfig jsonConfig = JsonConvert.DeserializeObject(jsonText); if (jsonConfig == null) jsonConfig = new JsonConfig(); return jsonConfig; } } [ContextMenu("SaveJsonConfig")] void SaveJsonConfig() { config.SetPosMatrix(areaMatrix.posMatrix); config.Write(); Debug.LogWarning("网格配置保存成功"); } }