Files
5.24BuYu/Assets/MyGame/Scripts/Characters/Monster/MonsterMoveLine.cs
T
2026-08-04 14:52:22 +08:00

530 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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<string, BezierCurveData> dic_BezierCurveDatas = new Dictionary<string, BezierCurveData>();
public static List<BezierCurveData> curves = new List<BezierCurveData>();
// ----------------------------------------------------------------
// ⭐ 性能优化:曲线点数组静态缓存
// 每条曲线的 [start, controlPoints..., end] 预组装成 Vector2[],
// 所有鱼共享同一份数组,避免每帧每鱼 new List + ToArray 的 GC 分配。
// 加载时构建,卸载时清理。
// ----------------------------------------------------------------
private static readonly Dictionary<string, Vector2[]> _curvePointsArrays
= new Dictionary<string, Vector2[]>();
[Serializable]
public class BezierCurveData
{
public string name;
public Vector2 start;
public List<Vector2> controlPoints = new List<Vector2>();
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<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(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<Vector2> points = new List<Vector2>();
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);
}
}
/// <summary>
/// ⭐ 将曲线的 [start, controlPoints..., end] 组装成 Vector2[] 并缓存。
/// 所有引用此曲线的鱼共享同一份数组,避免每帧每鱼 new List + ToArray 的 GC。
/// </summary>
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<string> keys = new List<string>();
keys.Add(path);
keys.Add(GameManager.Instance.gameData.currGameType.ToString());
AsyncOperationHandle<IList<TextAsset>> handles = Addressables.LoadAssetsAsync<TextAsset>(
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<Vector2> points = new List<Vector2>();
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<string> keys = new List<string>();
keys.Add(path);
keys.Add(GameManager.Instance.gameData.currGameType.ToString());
AsyncOperationHandle<IList<TextAsset>> handles = Addressables.LoadAssetsAsync<TextAsset>(
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<RectTransform>() 和 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}");
}
/// <summary>
/// 主线程零分配计算贝塞尔曲线点(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 由调用方缓存,避免每帧每鱼重复获取
/// </summary>
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<RectTransform>();
return CoordinateTransformation.CanvasToWorld(canvasPos, rect, cam);
}
/// <summary>主线程复用的临时计算缓冲(避免每帧每鱼 new 数组)</summary>
[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<string, BezierCurveData> dic_BezierCurveDatas = new Dictionary<string, BezierCurveData>();
// public static List<BezierCurveData> curves = new List<BezierCurveData>();
// public class BezierCurveData
// {
// public string name;
// public Vector2 start;
// public List<Vector2> controlPoints = new List<Vector2>();
// public Vector2 end;
// }
// public static async void LoadBezier()
// {
// await LoadBezierTxtAsync("MoveLine");
// }
// /// <summary>
// /// 读取曲线数据 读取单个
// /// </summary>
// /// <param name="path"></param>
// /// <returns></returns>
// public static async Task LoadSingleBezierTxtAsync(string path)
// {
// AsyncOperationHandle<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(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<Vector2> points = new List<Vector2>();
// 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);
// }
// }
// /// <summary>
// /// 读取曲线数据 游戏开始时在awake调用aa包
// /// </summary>
// /// <param name="path"></param>
// /// <returns></returns>
// public static async Task LoadBezierTxtAsync(string path)
// {
// List<string> keys = new List<string>();
// keys.Add(path);
// keys.Add(GameManager.Instance.gameData.currGameType.ToString());
// AsyncOperationHandle<IList<TextAsset>> handles = Addressables.LoadAssetsAsync<TextAsset>(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<Vector2> points = new List<Vector2>();
// 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}");
// }
// /// <summary>
// /// 获取曲线上的点
// /// </summary>
// /// <param name="t"></param>
// /// <returns></returns>
// public Vector2 GetBezierPoint(float t)
// {
// t = t / moveTime;
// BezierCurveData curve = bezierCurveData;
// List<Vector2> points = new List<Vector2>();
// points.Add(curve.start);
// points.AddRange(curve.controlPoints);
// points.Add(curve.end);
// return DeCasteljau_BY(points, t % 1);
// }
// /// <summary>
// /// 贝塞尔
// /// </summary>
// /// <param name="points"></param>
// /// <param name="t"></param>
// /// <returns></returns>
// private Vector2 DeCasteljau_BSE(List<Vector2> points, float t)
// {
// while (points.Count > 1)
// {
// List<Vector2> next = new List<Vector2>();
// 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<RectTransform>(), Camera.main);
// }
// /// <summary>
// /// B样
// /// </summary>
// /// <param name="points"></param>
// /// <param name="t"></param>
// /// <returns></returns>
// private Vector2 DeCasteljau_BY(List<Vector2> points, float t)
// {
// // 方法A:保持德卡斯特里奥算法(推荐,数值更稳定)
// while (points.Count > 1)
// {
// List<Vector2> next = new List<Vector2>();
// 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<RectTransform>(), 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<RectTransform>(), 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