using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using Deconstruction.Element; using Deconstruction.Interface; using Deconstruction.Manager; using Deconstruction.Tool; using Deconstruction.Type; using Deconstruction.Type.DMToolSlot; using Deconstruction.Type.Linkable; using SerializerHelper.Type; using UnityEngine; using UnityEngine.Pool; using XericLibrary.Runtime.MacroLibrary; using XericLibrary.Runtime.Nav; namespace SesothoLine { /// /// 线路绘制工具类 /// public class SesothoArrangementWiresTool : DecisionMakerToolBase { #region 事件委托 /// /// 工具中成员变更委托 /// public delegate void ToolMemberChange(PlacementBase obj); /// /// 工具中成员添加事件 /// public event ToolMemberChange OnAdditional; /// /// 工具中成员移除事件 /// public event ToolMemberChange OnDecreasing; /// /// 当任意元素苏醒 /// public event ToolMemberChange OnAnyVivification; /// /// 当任意元素休眠 /// public event ToolMemberChange OnAnyDormancy; #endregion #region 快捷键 /* * 1 控制强制直线 * 2,3 控制圆弧的角度为90和180度 * R 反转圆弧 * shift+b 忽略对象吸附 * shift+n 忽略网格吸附 */ /// /// 强制直线 /// public static KeyPack OpKey_ForceStr = new KeyPack(KeyCode.Alpha1); /// /// 强制90度圆弧 /// public static KeyPack OpKey_90Arc = new KeyPack(KeyCode.Alpha2); /// /// 强制180度圆弧 /// public static KeyPack OpKey_180Arc = new KeyPack(KeyCode.Alpha3); /// /// 反转顺向,比如单圆弧的方向翻转。 /// 话说shift+alt+r是英伟达的一个快捷键呢,有点冲突 /// public static KeyPack OpKey_RevCisoid = new KeyPack(KeyCode.R); /// /// 忽略吸附功能 /// public static KeyPack OpKey_IgnoreNeighborAdsorption = new KeyPack(KeyCode.LeftShift, KeyCode.B); /// /// 忽略网格吸附 /// public static KeyPack OpKey_IgnoreGridAdsorption = new KeyPack(KeyCode.LeftShift, KeyCode.N); #endregion #region 字段属性 private ObjectPool _linePool; private ObjectPool _pointPool; #endregion #region 获取转换 /// /// 获取光标下最近的对象为点 /// /// /// public static bool GetNearestObjectAsPoint(out TerminalPoint point) { if (SesothoPeManager.NearestObject is not null && SesothoPeManager.NearestObject is TerminalPoint terminalPoint) { point = terminalPoint; return true; } point = null; return false; } /// /// 获取光标下最近的对象为线 /// /// /// public static bool GetNearestObjectAsLine(out TerminalLine2T3 line) { if (SesothoPeManager.NearestObject is not null && SesothoPeManager.NearestObject is TerminalPoint terminalPoint && terminalPoint.LinkedData.GetOpposite(LinkType.Export) is TerminalLine2T3 terminalLine) { line = terminalLine; return true; } line = null; return false; } /// /// 获取以给定点上与最近的相切线相对 /// /// /// /// /// public static bool GetNearestObjectAsLine(TerminalPoint point, out TerminalLine2T3 line, out Vector3 normal) { if (point is null) goto End; // 检查最近线切线是否有效,是否相连 if (SesothoPeManager.NearestLineTangentObject is TerminalLine2T3 otherLine && point.TryGetLinkLineNormal(otherLine, out normal)) { line = otherLine; return true; } End: line = null; normal = default; return false; } #endregion #region 生命周期 protected override void Awake() { base.Awake(); _linePool = new ObjectPool( createFunc: CreatElement, actionOnGet: ElementActive, actionOnRelease: ElementInactive, actionOnDestroy: a => { }, collectionCheck: true, defaultCapacity: 10, maxSize: 10000); _pointPool = new ObjectPool( createFunc: CreatElement, actionOnGet: ElementActive, actionOnRelease: ElementInactive, actionOnDestroy: a => { }, collectionCheck: true, defaultCapacity: 10, maxSize: 10000); } protected override void OnEnableTool() { // 启用时创建一个输入等待行为,随后进入绘制行为 var target = GenerateOperateSlot(); target.InitializeNextTodo(() => new DrawTerminalLine2T3_OpSlot()); Debug.Log("激活线路绘制工具"); } protected override void OnDisableTool() { } protected override void OnFinalEnd() { base.OnFinalEnd(); EnableTool = false; } #endregion #region 序列化 public override SerializeUnion ToolSerializeDispost() { base.ToolSerializeDispost(); foreach (var element in PlaceElementManager.Inst.StructuralLinkedList) { var union = element.SerializedOccurs(); // 索引是后面用来标识类的 union.Index = TypeConsignMap.AddMapIndex(element.GetType()); union.RefreshSerializedContext(); SerializeTemp.Add(union); } SerializeTemp.RefreshSerializedContext(); return SerializeTemp; } public override bool ToolDiserializDispost(SerializeUnion context) { if (!base.ToolDiserializDispost(context)) return false; var deserializeList = new List<(PlacementBase, SerializeUnion)>(); var safeCount = ushort.MaxValue; Debug.Log("反序列化过程开始创建对象"); while (0 <-- safeCount && SerializeTemp.IndexMoveToNext(out var block)) { var union = SerializeTemp.GetDeserializeObject(); var type = TypeConsignMap.GetMapType(union.Index); if (GetElement(type, out var obj)) { deserializeList.Add((obj, union)); union.RefreshDeserializeContext(); union.IndexMoveToStart(); obj.DeserializeOccurs(union); // switch (obj) // { // case TerminalLine2T3 line: // line.DeserializeOccurs(union); // break; // case TerminalPoint point: // point.DeserializeOccurs(union); // break; // } } else Debug.LogError($"无法创建序列化元素,原因是类型不支持{type}"); if (block) break; } Debug.Log("反序列化过程开始恢复链接关系"); foreach (var item in deserializeList) item.Item1.DeserializeHysteresisOccurs(item.Item2); return true; } #endregion #region 方法 /// /// 创建元素 /// /// private T CreatElement() where T : PlacementBase, ILinkconfidentPe { var obj = CreatPlacementObject(); obj.OnPlancementDestory += a => { if (a is null) { Debug.LogError("元素已经被销毁,无法回收链表元素"); return; } if (a is T b && b.GetLinkedNode(out var node)) SesothoPeManager.Inst.RemoveLinkedTarget(node); else Debug.LogError("元素并非工具元素类型,或者其中的链表节点已丢失,导致无法回收链表元素"); }; return obj; } /// /// 池对象激活 /// /// /// private void ElementActive(T obj) where T : PlacementBase, ILinkconfidentPe { obj.gameObject.SetActive(true); OnAnyVivification?.Invoke(obj); } /// /// 池对象取消激活 /// /// /// private void ElementInactive(T obj) where T : PlacementBase, ILinkconfidentPe { obj.gameObject.SetActive(false); OnAnyDormancy?.Invoke(obj); } /// /// 获取元素 /// /// public bool GetElement(out T obj) where T : PlacementBase, ILinkconfidentPe { var name = typeof(T).Name; switch (name) { case nameof(TerminalLine2T3): obj = _linePool.Get() as T; return true; case nameof(TerminalPoint): obj = _pointPool.Get() as T; return true; default: Debug.LogError($"给定的元素类型({name})并非预期,这将跳过对象池过程"); break; } obj = null; return false; } /// /// 获取元素 /// /// /// /// public bool GetElement(Type type, out PlacementBase obj) { var name = type.Name; switch (name) { case nameof(TerminalLine2T3): obj = _linePool.Get(); return true; case nameof(TerminalPoint): obj = _pointPool.Get(); return true; default: Debug.LogError($"给定的元素类型({name})并非预期,这将跳过对象池过程"); break; } obj = null; return false; } /// /// 删除元素 /// /// /// public void DeleteElement(T obj) where T : PlacementBase, ILinkconfidentPe { var name = typeof(T).Name; switch (name) { case nameof(TerminalLine2T3): _linePool.Release(obj as TerminalLine2T3); break; case nameof(TerminalPoint): _pointPool.Release(obj as TerminalPoint); break; default: Debug.LogError($"给定的元素类型({name})并非预期,这将跳过对象池过程"); break; } } /// /// 保存元素链,在创建并常态化元素后都需要执行的操作 (注意不是在创建后立刻保存) /// /// 如果这是一个线,那么还应该调用 AddBulkLineToNeighborGrid; /// 如果这是一个点,那么应该调用 AddPeToNeighborGrid。 /// /// public void PersistentElement(T obj) where T : PlacementBase, ILinkconfidentPe { // 保存链表结构,反过来也持有这个节点,便于后续查找 var lineNode = SesothoPeManager.Inst.AddLinkedTarget(obj); obj.SetLinkedNode(lineNode); } /// /// 移除元素链 /// /// /// public void ExcisionElement(T obj) where T : PlacementBase, ILinkconfidentPe { if (obj.GetLinkedNode(out var node)) SesothoPeManager.Inst.RemoveLinkedTarget(node); } /// /// 添加小型元素到网格中 /// /// /// public void AddPeToNeighborGrid(T obj) where T : PlacementBase, ILinkconfidentPe { if (obj.NeighborGridIndex != null) { Debug.LogError("无法重复插入元素:当前向管理器中插入的的元素,可能已经存在于其他管理器内,请先退出其他管理器后重试。"); return; } SesothoPeManager.Inst.InsertNeighbor(obj, out var index); obj.NeighborGridIndex = index; // 回调事件 OnAdditional?.Invoke(obj); } /// /// 添加大型元素到网格中 /// public void AddBulkPeToNeighborGrid(T obj) where T : PlacementBase, ILinkconfidentPe, IPossessorTrajectory2 { if (obj.NeighborGridIndex != null) { Debug.LogError("无法重复插入元素:当前向管理器中插入的的元素,可能已经存在于其他管理器内,请先退出其他管理器后重试。"); return; } SesothoPeManager.Inst.InsertGiantNeighbor(obj, out var index); obj.NeighborGridIndex = index; // 回调事件 OnAdditional?.Invoke(obj); } /// /// 移除这个小元素 /// /// /// public void RemovePeToNeighborGrid(T obj) where T : PlacementBase, ILinkconfidentPe { if (obj.NeighborGridIndex != null && obj.NeighborGridIndex.GetAsIndex(out var index)) SesothoPeManager.Inst.RemoveNeighbor(index); else SesothoPeManager.Inst.RemoveNeighbor(obj); ExcisionElement(obj); // 回调事件 OnDecreasing?.Invoke(obj); } /// /// 移除这个大型元素 /// public void RemoveBulkPeFormNeighborGrid(T obj) where T : PlacementBase, ILinkconfidentPe, IPossessorTrajectory2 { if (obj.NeighborGridIndex != null && obj.NeighborGridIndex.GetAsMappingIndex(out var index)) SesothoPeManager.Inst.RemoveGiantNeighbor(index); else SesothoPeManager.Inst.RemoveGiantNeighbor(obj); ExcisionElement(obj); // 回调事件 OnDecreasing?.Invoke(obj); } #endregion #region 绘制寻路 /* * 没写完,如果不嫌麻烦可以看看Astart类,或者自己实现。 * * 主要提供绘制线路时的多段线拟合,障碍物避让 */ /// /// 获取一个可以通过的路径 /// /// 这是一个只在向上的二维平面上有效的路径。 /// 寻路的规则是仅避开建筑物,贪婪算法。 /// /// /// 起点 /// 终点 /// 避让半径,在遭遇碰撞时将沿法向退回这个距离 public DrawWayPoints GetPassThroughRouteShortcut(Vector3 startPoint, Vector3 endPoint, float avoidRadius) { var result = new DrawWayPoints(); Debug.LogError("此方法未完成"); return result; } /// /// 获取一个可以通过的路径的迭代器; /// /// 起点 /// 终点 /// 避让半径,在遭遇障碍时将沿法向退回这个距离 /// 检查碰撞对象的层 /// protected IEnumerable GetPassThroughRoute(Vector3 startPoint, Vector3 endPoint, float avoidRadius, LayerMask layer) { yield return Vector3.zero; } /// /// 使用给定的路径创建一段工具拟合的路径 /// /// 一段路径 public void BuildPaths(DrawWayPoints wayPoints) { new AStart2(); } #endregion } }