559 lines
18 KiB
C#
559 lines
18 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 线路绘制工具类
|
|
/// </summary>
|
|
public class SesothoArrangementWiresTool : DecisionMakerToolBase
|
|
{
|
|
#region 事件委托
|
|
|
|
/// <summary>
|
|
/// 工具中成员变更委托
|
|
/// </summary>
|
|
public delegate void ToolMemberChange(PlacementBase obj);
|
|
|
|
/// <summary>
|
|
/// 工具中成员添加事件
|
|
/// </summary>
|
|
public event ToolMemberChange OnAdditional;
|
|
|
|
/// <summary>
|
|
/// 工具中成员移除事件
|
|
/// </summary>
|
|
public event ToolMemberChange OnDecreasing;
|
|
|
|
/// <summary>
|
|
/// 当任意元素苏醒
|
|
/// </summary>
|
|
public event ToolMemberChange OnAnyVivification;
|
|
|
|
/// <summary>
|
|
/// 当任意元素休眠
|
|
/// </summary>
|
|
public event ToolMemberChange OnAnyDormancy;
|
|
|
|
#endregion
|
|
|
|
#region 快捷键
|
|
|
|
/*
|
|
* 1 控制强制直线
|
|
* 2,3 控制圆弧的角度为90和180度
|
|
* R 反转圆弧
|
|
* shift+b 忽略对象吸附
|
|
* shift+n 忽略网格吸附
|
|
*/
|
|
|
|
/// <summary>
|
|
/// 强制直线
|
|
/// </summary>
|
|
public static KeyPack OpKey_ForceStr = new KeyPack(KeyCode.Alpha1);
|
|
/// <summary>
|
|
/// 强制90度圆弧
|
|
/// </summary>
|
|
public static KeyPack OpKey_90Arc = new KeyPack(KeyCode.Alpha2);
|
|
/// <summary>
|
|
/// 强制180度圆弧
|
|
/// </summary>
|
|
public static KeyPack OpKey_180Arc = new KeyPack(KeyCode.Alpha3);
|
|
/// <summary>
|
|
/// 反转顺向,比如单圆弧的方向翻转。
|
|
/// 话说shift+alt+r是英伟达的一个快捷键呢,有点冲突
|
|
/// </summary>
|
|
public static KeyPack OpKey_RevCisoid = new KeyPack(KeyCode.R);
|
|
/// <summary>
|
|
/// 忽略吸附功能
|
|
/// </summary>
|
|
public static KeyPack OpKey_IgnoreNeighborAdsorption = new KeyPack(KeyCode.LeftShift, KeyCode.B);
|
|
/// <summary>
|
|
/// 忽略网格吸附
|
|
/// </summary>
|
|
public static KeyPack OpKey_IgnoreGridAdsorption = new KeyPack(KeyCode.LeftShift, KeyCode.N);
|
|
|
|
#endregion
|
|
|
|
#region 字段属性
|
|
|
|
private ObjectPool<TerminalLine2T3> _linePool;
|
|
private ObjectPool<TerminalPoint> _pointPool;
|
|
|
|
#endregion
|
|
|
|
#region 获取转换
|
|
|
|
/// <summary>
|
|
/// 获取光标下最近的对象为点
|
|
/// </summary>
|
|
/// <param name="point"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取光标下最近的对象为线
|
|
/// </summary>
|
|
/// <param name="line"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取以给定点上与最近的相切线相对
|
|
/// </summary>
|
|
/// <param name="point"></param>
|
|
/// <param name="line"></param>
|
|
/// <param name="normal"></param>
|
|
/// <returns></returns>
|
|
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<TerminalLine2T3>(
|
|
createFunc: CreatElement<TerminalLine2T3>,
|
|
actionOnGet: ElementActive,
|
|
actionOnRelease: ElementInactive,
|
|
actionOnDestroy: a =>
|
|
{ },
|
|
collectionCheck: true,
|
|
defaultCapacity: 10,
|
|
maxSize: 10000);
|
|
|
|
_pointPool = new ObjectPool<TerminalPoint>(
|
|
createFunc: CreatElement<TerminalPoint>,
|
|
actionOnGet: ElementActive,
|
|
actionOnRelease: ElementInactive,
|
|
actionOnDestroy: a =>
|
|
{ },
|
|
collectionCheck: true,
|
|
defaultCapacity: 10,
|
|
maxSize: 10000);
|
|
|
|
}
|
|
|
|
protected override void OnEnableTool()
|
|
{
|
|
// 启用时创建一个输入等待行为,随后进入绘制行为
|
|
var target = GenerateOperateSlot<WaitForUserInput_OpSlot>();
|
|
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<SerializeUnion>();
|
|
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 方法
|
|
|
|
/// <summary>
|
|
/// 创建元素
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private T CreatElement<T>()
|
|
where T : PlacementBase, ILinkconfidentPe
|
|
{
|
|
var obj = CreatPlacementObject<T>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 池对象激活
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
private void ElementActive<T>(T obj)
|
|
where T : PlacementBase, ILinkconfidentPe
|
|
{
|
|
obj.gameObject.SetActive(true);
|
|
OnAnyVivification?.Invoke(obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 池对象取消激活
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
private void ElementInactive<T>(T obj)
|
|
where T : PlacementBase, ILinkconfidentPe
|
|
{
|
|
obj.gameObject.SetActive(false);
|
|
OnAnyDormancy?.Invoke(obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取元素
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool GetElement<T>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取元素
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <param name="obj"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 删除元素
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
public void DeleteElement<T>(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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 保存元素链,在创建并常态化元素后都需要执行的操作 (注意不是在创建后立刻保存)
|
|
/// <code>
|
|
/// 如果这是一个线,那么还应该调用 AddBulkLineToNeighborGrid;
|
|
/// 如果这是一个点,那么应该调用 AddPeToNeighborGrid。
|
|
/// </code>
|
|
/// </summary>
|
|
public void PersistentElement<T>(T obj)
|
|
where T : PlacementBase,
|
|
ILinkconfidentPe
|
|
{
|
|
// 保存链表结构,反过来也持有这个节点,便于后续查找
|
|
var lineNode = SesothoPeManager.Inst.AddLinkedTarget(obj);
|
|
obj.SetLinkedNode(lineNode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 移除元素链
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
public void ExcisionElement<T>(T obj)
|
|
where T : PlacementBase,
|
|
ILinkconfidentPe
|
|
{
|
|
if (obj.GetLinkedNode(out var node))
|
|
SesothoPeManager.Inst.RemoveLinkedTarget(node);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 添加小型元素到网格中
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
public void AddPeToNeighborGrid<T>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加大型元素到网格中
|
|
/// </summary>
|
|
public void AddBulkPeToNeighborGrid<T>(T obj)
|
|
where T : PlacementBase,
|
|
ILinkconfidentPe,
|
|
IPossessorTrajectory2
|
|
{
|
|
if (obj.NeighborGridIndex != null)
|
|
{
|
|
Debug.LogError("无法重复插入元素:当前向管理器中插入的的元素,可能已经存在于其他管理器内,请先退出其他管理器后重试。");
|
|
return;
|
|
}
|
|
SesothoPeManager.Inst.InsertGiantNeighbor<T>(obj, out var index);
|
|
obj.NeighborGridIndex = index;
|
|
|
|
// 回调事件
|
|
OnAdditional?.Invoke(obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 移除这个小元素
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
public void RemovePeToNeighborGrid<T>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 移除这个大型元素
|
|
/// </summary>
|
|
public void RemoveBulkPeFormNeighborGrid<T>(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<T>(obj);
|
|
ExcisionElement(obj);
|
|
// 回调事件
|
|
OnDecreasing?.Invoke(obj);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 绘制寻路
|
|
|
|
/*
|
|
* 没写完,如果不嫌麻烦可以看看Astart类,或者自己实现。
|
|
*
|
|
* 主要提供绘制线路时的多段线拟合,障碍物避让
|
|
*/
|
|
|
|
/// <summary>
|
|
/// 获取一个可以通过的路径
|
|
/// <code>
|
|
/// 这是一个只在向上的二维平面上有效的路径。
|
|
/// 寻路的规则是仅避开建筑物,贪婪算法。
|
|
/// </code>
|
|
/// </summary>
|
|
/// <param name="startPoint">起点</param>
|
|
/// <param name="endPoint">终点</param>
|
|
/// <param name="avoidRadius">避让半径,在遭遇碰撞时将沿法向退回这个距离</param>
|
|
public DrawWayPoints GetPassThroughRouteShortcut(Vector3 startPoint, Vector3 endPoint, float avoidRadius)
|
|
{
|
|
var result = new DrawWayPoints();
|
|
Debug.LogError("此方法未完成");
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取一个可以通过的路径的迭代器;
|
|
/// </summary>
|
|
/// <param name="startPoint">起点</param>
|
|
/// <param name="endPoint">终点</param>
|
|
/// <param name="avoidRadius">避让半径,在遭遇障碍时将沿法向退回这个距离</param>
|
|
/// <param name="layer">检查碰撞对象的层</param>
|
|
/// <returns></returns>
|
|
protected IEnumerable<Vector3> GetPassThroughRoute(Vector3 startPoint, Vector3 endPoint, float avoidRadius, LayerMask layer)
|
|
{
|
|
yield return Vector3.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用给定的路径创建一段工具拟合的路径
|
|
/// </summary>
|
|
/// <param name="wayPoints">一段路径</param>
|
|
public void BuildPaths(DrawWayPoints wayPoints)
|
|
{
|
|
new AStart2();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|