#region AI优化 using System.Collections; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using System; [Serializable] public class MonsterMoveLine { public static Dictionary dic_BezierCurveDatas = new Dictionary(); public static List curves = new List(); // ---------------------------------------------------------------- // ⭐ 性能优化:曲线点数组静态缓存 // 每条曲线的 [start, controlPoints..., end] 预组装成 Vector2[], // 所有鱼共享同一份数组,避免每帧每鱼 new List + ToArray 的 GC 分配。 // 加载时构建,卸载时清理。 // ---------------------------------------------------------------- private static readonly Dictionary _curvePointsArrays = new Dictionary(); [Serializable] public class BezierCurveData { public string name; public Vector2 start; public List controlPoints = new List(); public Vector2 end; } public static async Task LoadBezier() { await LoadBezierTxtAsync("MoveLine"); } public static async Task UnLoadBezier() { await UnLoadBezierTxtAsync("MoveLine"); } public static async Task LoadSingleBezierTxtAsync(string path) { AsyncOperationHandle handle = Addressables.LoadAssetAsync(path); await handle.Task; var lines = handle.Result.text.Split('\n'); foreach (var line in lines) { if (string.IsNullOrWhiteSpace(line)) continue; var parts = line.Split(';'); BezierCurveData curve = new BezierCurveData(); curve.name = parts[0]; List points = new List(); for (int i = 1; i < parts.Length; i++) { points.Add(ParseVector(parts[i])); } curve.start = points[0]; if (points.Count > 1) curve.end = points[points.Count - 1]; for (int i = 1; i < points.Count - 1; i++) curve.controlPoints.Add(points[i]); curves.Add(curve); dic_BezierCurveDatas.Add(curve.name, curve); // ⭐ 同步构建点数组缓存(单条加载路径) BuildCurvePointsArray(curve); } } /// /// ⭐ 将曲线的 [start, controlPoints..., end] 组装成 Vector2[] 并缓存。 /// 所有引用此曲线的鱼共享同一份数组,避免每帧每鱼 new List + ToArray 的 GC。 /// private static void BuildCurvePointsArray(BezierCurveData curve) { if (_curvePointsArrays.ContainsKey(curve.name)) return; int total = 2 + curve.controlPoints.Count; // start + controls + end Vector2[] arr = new Vector2[total]; arr[0] = curve.start; for (int i = 0; i < curve.controlPoints.Count; i++) arr[1 + i] = curve.controlPoints[i]; arr[total - 1] = curve.end; _curvePointsArrays[curve.name] = arr; } public static async Task LoadBezierTxtAsync(string path) { List keys = new List(); keys.Add(path); keys.Add(GameManager.Instance.gameData.currGameType.ToString()); AsyncOperationHandle> handles = Addressables.LoadAssetsAsync( keys, null, Addressables.MergeMode.Intersection); await handles.Task; // ⭐ 没有资源 → 直接跳出,不报错 if (handles.Result == null || handles.Result.Count == 0) { Debug.LogWarning($"Bezier 曲线加载跳过:未找到标签 {path} + {GameManager.Instance.gameData.currGameType}"); Addressables.Release(handles); return; } foreach (var text in handles.Result) { var lines = text.text.Split('\n'); foreach (var line in lines) { if (string.IsNullOrWhiteSpace(line)) continue; var parts = line.Split(';'); if (parts.Length < 2) continue; BezierCurveData curve = new BezierCurveData(); curve.name = parts[0]; List points = new List(); for (int i = 1; i < parts.Length; i++) { if (!string.IsNullOrWhiteSpace(parts[i])) points.Add(ParseVector(parts[i])); } if (points.Count == 0) continue; curve.start = points[0]; if (points.Count > 1) curve.end = points[points.Count - 1]; for (int i = 1; i < points.Count - 1; i++) curve.controlPoints.Add(points[i]); curves.Add(curve); // 不覆盖旧数据 if (!dic_BezierCurveDatas.ContainsKey(curve.name)) { dic_BezierCurveDatas.Add(curve.name, curve); // ⭐ 同步构建点数组缓存(批量加载路径) BuildCurvePointsArray(curve); } } } Addressables.Release(handles); Debug.Log("曲线读取完成共" + dic_BezierCurveDatas.Count + "条"); } public static async Task UnLoadBezierTxtAsync(string path) { List keys = new List(); keys.Add(path); keys.Add(GameManager.Instance.gameData.currGameType.ToString()); AsyncOperationHandle> handles = Addressables.LoadAssetsAsync( keys, null, Addressables.MergeMode.Intersection); await handles.Task; // ⭐ 没有资源 → 直接跳出,不报错 if (handles.Result == null || handles.Result.Count == 0) { Debug.LogWarning($"Bezier 曲线卸载跳过:未找到标签 {path} + {GameManager.Instance.gameData.currGameType}"); Addressables.Release(handles); return; } foreach (var text in handles.Result) { var lines = text.text.Split('\n'); foreach (var line in lines) { if (string.IsNullOrWhiteSpace(line)) continue; var parts = line.Split(';'); if (parts.Length == 0) continue; string curveName = parts[0]; if (string.IsNullOrWhiteSpace(curveName)) continue; // 字典存在才移除 if (dic_BezierCurveDatas.ContainsKey(curveName)) { dic_BezierCurveDatas.Remove(curveName); } // ⭐ 同步清理点数组缓存 _curvePointsArrays.Remove(curveName); // 列表移除所有同名曲线 curves.RemoveAll(c => c.name == curveName); } } Addressables.Release(handles); Debug.Log("曲线卸载完成,剩余曲线数量:" + dic_BezierCurveDatas.Count); } private static Vector2 ParseVector(string s) { var xy = s.Split(','); return new Vector2(float.Parse(xy[0]), float.Parse(xy[1])); } public BezierCurveData bezierCurveData; public float moveTime; public float moveSpeed; // ⭐ 缓存相机和 UI 根 RectTransform,避免每帧每鱼调用 // UIManager.Instance.uiRoot.GetComponent() 和 Camera.main // 由 MonsterContral.OnEnable 同步赋值 public Camera cachedCamera; public RectTransform cachedUirootRect; public void Init(BezierCurveData bezierCurveData, float Time) { this.bezierCurveData = bezierCurveData; this.moveTime = Time; Debug.Log($"初始化曲线 {this.bezierCurveData.name} ,移动时间 {moveTime}"); } /// /// 主线程零分配计算贝塞尔曲线点(De Casteljau 算法)。 /// ⭐ 优化说明(原 Job 方案的负优化问题): /// 1. 原 GetBezierPointJob 每次调用 new List + 2 个 NativeArray(TempJob) + ToArray, /// 每条鱼每帧产生 3 次堆分配,N 条鱼 = 3N 次 GC 分配/帧 /// 2. 原 Job.Schedule() 后立即 Complete(),Job 实际单线程执行, /// 调度开销 > 计算收益(4-7 个控制点的计算量极小) /// 3. 现方案:曲线点数组静态缓存(共享),主线程直接 Lerp 计算,零 GC 分配 /// 4. Camera/RectTransform 由调用方缓存,避免每帧每鱼重复获取 /// public Vector2 GetBezierPointJob(float t) { // ⭐ 防止除零:moveTime<=0 时直接返回起点,避免 NaN if (moveTime <= 0f) return bezierCurveData.start; if (t > moveTime) t = moveTime; t = t / moveTime; // ⭐ 取静态缓存的曲线点数组(所有鱼共享,零分配) if (!_curvePointsArrays.TryGetValue(bezierCurveData.name, out var pts)) { // 兜底:缓存未命中时实时构建(理论上不应发生,加载时已构建) BuildCurvePointsArray(bezierCurveData); pts = _curvePointsArrays[bezierCurveData.name]; } // ⭐ 复用线程静态缓冲区(主线程单线程执行,安全复用) if (_tempPoints == null || _tempPoints.Length < pts.Length) _tempPoints = new Vector2[pts.Length]; Array.Copy(pts, _tempPoints, pts.Length); // De Casteljau 算法:逐层 Lerp 直到剩一个点 int count = pts.Length; while (count > 1) { for (int i = 0; i < count - 1; i++) _tempPoints[i] = Vector2.Lerp(_tempPoints[i], _tempPoints[i + 1], t); count--; } Vector2 canvasPos = _tempPoints[0]; // ⭐ 用缓存的 Camera/RectTransform,避免每帧每鱼查询 var cam = cachedCamera != null ? cachedCamera : Camera.main; var rect = cachedUirootRect != null ? cachedUirootRect : UIManager.Instance.uiRoot.GetComponent(); return CoordinateTransformation.CanvasToWorld(canvasPos, rect, cam); } /// 主线程复用的临时计算缓冲(避免每帧每鱼 new 数组) [ThreadStatic] private static Vector2[] _tempPoints; } #endregion #region zijixie //using System.Collections; //using System.Collections.Generic; //using System.IO; //using System.Threading.Tasks; //using UnityEngine; //using UnityEngine.AddressableAssets; //using UnityEngine.ResourceManagement.AsyncOperations; //public class MonsterMoveLine //{ // public static Dictionary dic_BezierCurveDatas = new Dictionary(); // public static List curves = new List(); // public class BezierCurveData // { // public string name; // public Vector2 start; // public List controlPoints = new List(); // public Vector2 end; // } // public static async void LoadBezier() // { // await LoadBezierTxtAsync("MoveLine"); // } // /// // /// 读取曲线数据 读取单个 // /// // /// // /// // public static async Task LoadSingleBezierTxtAsync(string path) // { // AsyncOperationHandle handle = Addressables.LoadAssetAsync(path); // await handle.Task; // var lines = handle.Result.text.Split('\n'); // foreach (var line in lines) // { // if (string.IsNullOrWhiteSpace(line)) continue; // var parts = line.Split(';'); // BezierCurveData curve = new BezierCurveData(); // // 第一个是曲线名 // curve.name = parts[0]; // // 后面所有都是点 // List points = new List(); // for (int i = 1; i < parts.Length; i++) // { // points.Add(ParseVector(parts[i])); // } // // 第一个点是 start // curve.start = points[0]; // if (points.Count > 1) // { // // 最后一个点是 end // curve.end = points[points.Count - 1]; // } // // 中间的都是 controlPoints // for (int i = 1; i < points.Count - 1; i++) // { // curve.controlPoints.Add(points[i]); // } // curves.Add(curve); // dic_BezierCurveDatas.Add(curve.name, curve); // } // } // /// // /// 读取曲线数据 游戏开始时在awake调用aa包 // /// // /// // /// // public static async Task LoadBezierTxtAsync(string path) // { // List keys = new List(); // keys.Add(path); // keys.Add(GameManager.Instance.gameData.currGameType.ToString()); // AsyncOperationHandle> handles = Addressables.LoadAssetsAsync(keys, null, Addressables.MergeMode.Intersection); // await handles.Task; // foreach (var text in handles.Result) // { // var lines = text.text.Split('\n'); // foreach (var line in lines) // { // if (string.IsNullOrWhiteSpace(line)) continue; // var parts = line.Split(';'); // BezierCurveData curve = new BezierCurveData(); // // 第一个是曲线名 // curve.name = parts[0]; // // 后面所有都是点 // List points = new List(); // for (int i = 1; i < parts.Length; i++) // { // points.Add(ParseVector(parts[i])); // } // // 第一个点是 start // curve.start = points[0]; // if (points.Count > 1) // { // // 最后一个点是 end // curve.end = points[points.Count - 1]; // } // // 中间的都是 controlPoints // for (int i = 1; i < points.Count - 1; i++) // { // curve.controlPoints.Add(points[i]); // } // curves.Add(curve); // dic_BezierCurveDatas.Add(curve.name, curve); // } // } // Debug.Log("曲线读取完成共" + dic_BezierCurveDatas.Count + "条"); // } // private static Vector2 ParseVector(string s) // { // var xy = s.Split(','); // return new Vector2(float.Parse(xy[0]), float.Parse(xy[1])); // } // public BezierCurveData bezierCurveData; // public float moveTime; // public float moveSpeed; // public void Init(BezierCurveData bezierCurveData, float Time) // { // this.bezierCurveData = bezierCurveData; // this.moveTime = time; // Debug.Log($"初始化曲线 {this.bezierCurveData.name} ,移动时间 {moveTime}"); // } // /// // /// 获取曲线上的点 // /// // /// // /// // public Vector2 GetBezierPoint(float t) // { // t = t / moveTime; // BezierCurveData curve = bezierCurveData; // List points = new List(); // points.Add(curve.start); // points.AddRange(curve.controlPoints); // points.Add(curve.end); // return DeCasteljau_BY(points, t % 1); // } // /// // /// 贝塞尔 // /// // /// // /// // /// // private Vector2 DeCasteljau_BSE(List points, float t) // { // while (points.Count > 1) // { // List next = new List(); // for (int i = 0; i < points.Count - 1; i++) // { // next.Add(Vector2.Lerp(points[i], points[i + 1], t)); // } // points = next; // } // return CoordinateTransformation.CanvasToWorld(points[0], UIManager.Instance.uiRoot.GetComponent(), Camera.main); // } // /// // /// B样 // /// // /// // /// // /// // private Vector2 DeCasteljau_BY(List points, float t) // { // // 方法A:保持德卡斯特里奥算法(推荐,数值更稳定) // while (points.Count > 1) // { // List next = new List(); // for (int i = 0; i < points.Count - 1; i++) // { // next.Add(Vector2.Lerp(points[i], points[i + 1], t)); // } // points = next; // } // return CoordinateTransformation.CanvasToWorld(points[0], // UIManager.Instance.uiRoot.GetComponent(), Camera.main); // /* 方法B:伯恩斯坦多项式形式(标准贝塞尔公式)- 如果要用这个替换上面的循环 // int n = points.Count - 1; // Vector2 result = Vector2.zero; // for (int i = 0; i <= n; i++) // { // float bernstein = BinomialCoefficient(n, i) * Mathf.Pow(t, i) * Mathf.Pow(1 - t, n - i); // result += points[i] * bernstein; // } // return CoordinateTransformation.CanvasToWorld(result, // UIManager.Instance.uiRoot.GetComponent(), Camera.main); // */ // } // // 计算二项式系数 // private float BinomialCoefficient(int n, int k) // { // float result = 1f; // for (int i = 1; i <= k; i++) // { // result *= (float)(n - k + i) / i; // } // return result; // } //} #endregion