同步移动到示例文件
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7758a0679c9038144a4b0d007d44618f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Lrss3.ConfigUIElement",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||
"GUID:b1727b829ae4f0641acc8ad28ed624c2"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25409440eb1d34849a42df21f5989356
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8353159549bc0b74dae8320db161b27d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f19c25ad34929594289c10f5aecb5373
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81125e8b237f1804c97d171c4f4cd789
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d2e54232b0471c45ba6e8728efd1526
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1c0cba0cf9b5984c9f36789b8104059
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b21886d1f8d94284aba68dda1fd622b0
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d53a566643e99d74292ec8e451eb7356
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
public class ConfigButton : ConfigSelectableItem
|
||||
{
|
||||
#region 属性字段
|
||||
|
||||
public Button targetButton;
|
||||
|
||||
public override bool AllowInput
|
||||
{
|
||||
get => base.AllowInput;
|
||||
set
|
||||
{
|
||||
if (targetButton != null)
|
||||
targetButton.interactable = !value;
|
||||
base.AllowInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实现
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(newValue, forceSet);
|
||||
}
|
||||
|
||||
private bool _doOnce = false;
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
var name = component.transform.name;
|
||||
if (name is not "button") return;
|
||||
if (_doOnce)
|
||||
{
|
||||
Debug.LogError("存在重复命名的属性模块:" + name);
|
||||
return;
|
||||
}
|
||||
if (component is Button button)
|
||||
{
|
||||
_doOnce = true;
|
||||
if (targetButton != null)
|
||||
targetButton = button;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73f7a373611d15741b1093e0669abea9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
/// <summary>
|
||||
/// 一种数值增减输入框,将使用浮点值作为默认
|
||||
/// </summary>
|
||||
public class ConfigDigitalInputField : ConfigSelectableItem
|
||||
{
|
||||
#region 属性字段
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public float editorInput;
|
||||
#endif
|
||||
|
||||
public TMP_InputField targetInput;
|
||||
|
||||
public Button digitalAddButton;
|
||||
public Button digitalSubButton;
|
||||
|
||||
public double maxValue = 114514;
|
||||
public double minValue = -114515;
|
||||
public int digits = 6;
|
||||
|
||||
/// <summary>
|
||||
/// 增量
|
||||
/// </summary>
|
||||
public double delta = 1;
|
||||
|
||||
public override bool AllowInput
|
||||
{
|
||||
get => base.AllowInput;
|
||||
set
|
||||
{
|
||||
if (targetInput != null)
|
||||
targetInput.readOnly = !value;
|
||||
if (digitalAddButton != null)
|
||||
digitalAddButton.interactable = value;
|
||||
if (digitalSubButton != null)
|
||||
digitalSubButton.interactable = value;
|
||||
base.AllowInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
#if UNITY_EDITOR
|
||||
if (targetInput != null)
|
||||
targetInput.text = editorInput.ToString();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
if (ReferenceEquals(targetInput, null))
|
||||
{
|
||||
Debug.LogError($"{name}配置项目条目的基本元素不存在");
|
||||
return;
|
||||
}
|
||||
targetInput.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInput.onValueChanged.AddListener(o =>
|
||||
{
|
||||
double value = 0;
|
||||
if (double.TryParse(o, out value))
|
||||
{
|
||||
SetValue(value);
|
||||
}
|
||||
});
|
||||
targetInput.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
if (!ReferenceEquals(digitalAddButton, null))
|
||||
digitalAddButton.onClick.AddListener(delegate
|
||||
{
|
||||
SetValue(CastDigitalNumber(Value) + delta);
|
||||
RefreshFormatValue(Value);
|
||||
});
|
||||
if (!ReferenceEquals(digitalSubButton, null))
|
||||
digitalSubButton.onClick.AddListener(delegate
|
||||
{
|
||||
SetValue(CastDigitalNumber(Value) - delta);
|
||||
RefreshFormatValue(Value);
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实现
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
base.SetValue(CastDigitalNumber(newValue, minValue, maxValue, digits));
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(CastDigitalNumber(newValue, minValue, maxValue, digits), forceSet);
|
||||
}
|
||||
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
|
||||
if (component is TMP_InputField inputField)
|
||||
{
|
||||
if (targetInput != null)
|
||||
targetInput = inputField;
|
||||
}
|
||||
else if(component is Button button)
|
||||
{
|
||||
var name = component.transform.name;
|
||||
switch (name)
|
||||
{
|
||||
case "subbutton":
|
||||
case "SubButton":
|
||||
if (digitalSubButton != null)
|
||||
digitalSubButton = button;
|
||||
break;
|
||||
case "addbutton":
|
||||
case "AddButton":
|
||||
if (digitalAddButton != null)
|
||||
digitalAddButton = button;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RefreshFormatValue(object newValue)
|
||||
{
|
||||
base.RefreshFormatValue(newValue);
|
||||
targetInput.text = CastDigitalNumber(newValue).ToString(numberFormat);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d84fd414ac6743e46909249427b18364
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
public class ConfigDropdown : ConfigSelectableItem
|
||||
{
|
||||
#region ÊôÐÔ×Ö¶Î
|
||||
|
||||
public TMP_Dropdown targetDropdown;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ÉúÃüÖÜÆÚ
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
targetDropdown.onValueChanged.AddListener(index =>
|
||||
{
|
||||
SetValue(index);
|
||||
});
|
||||
}
|
||||
|
||||
public void InitializationDropdown(IEnumerable<string> options)
|
||||
{
|
||||
targetDropdown.options = options
|
||||
.Select(option => new TMP_Dropdown.OptionData(option)).ToList();
|
||||
targetDropdown.RefreshShownValue();
|
||||
if (IsValueInitNull)
|
||||
RefreshValueWithoutEvent(0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ʵÏÖ
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
base.SetValue(CastIntNumber(newValue));
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(CastIntNumber(newValue), forceSet);
|
||||
}
|
||||
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
var name = component.transform.name;
|
||||
if (name is not "dropdown") return;
|
||||
if (component is TMP_Dropdown dropdown)
|
||||
{
|
||||
if (targetDropdown != null)
|
||||
targetDropdown = dropdown;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RefreshFormatValue(object newValue)
|
||||
{
|
||||
base.RefreshFormatValue(newValue);
|
||||
if (newValue == null)
|
||||
return;
|
||||
targetDropdown.value = CastIntNumber(newValue);
|
||||
targetDropdown.RefreshShownValue();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 471d16b38f4449a46b9fcf5b312f65d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,712 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.CustomEditor;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
/// <summary>
|
||||
/// 一种用于基础面板设置条目的类
|
||||
/// <code>
|
||||
/// 在此类中提供了双向的属性委托修改,并且拥有脏标记。
|
||||
/// 该类在初始化时以输入为优先。
|
||||
/// 脏标记仅当界面首次发生改变时进行记录,并不会影响值。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public abstract class ConfigSelectableItem : MonoBehaviour
|
||||
{
|
||||
#region 静态事件
|
||||
|
||||
private static event Action<bool, bool> ForceUpdateAllConfigEvent;
|
||||
/// <summary>
|
||||
/// 强制所有激活的成员产生请求更新的事件(比如初始化时,外部更新时)
|
||||
/// </summary>
|
||||
/// <param name="resetDirty"></param>
|
||||
public static void ForceUpdateAll(bool resetDirty = false, bool forceSet = false)
|
||||
{
|
||||
ForceUpdateAllConfigEvent?.Invoke(resetDirty, forceSet);
|
||||
}
|
||||
|
||||
private static event Action ForceSetAllSourceValueEvent;
|
||||
/// <summary>
|
||||
/// 强制所有激活的成员产生请求设置的事件(比如按下应用按钮时)
|
||||
/// </summary>
|
||||
public static void ForceSetAllSourceValue()
|
||||
{
|
||||
ForceSetAllSourceValueEvent?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数值遭到修改时
|
||||
/// </summary>
|
||||
public static event Action<ConfigSelectableItem> OnAnyValueDirty;
|
||||
|
||||
/// <summary>
|
||||
/// 当数值收到修改时,产生事件,其中值的含义是修改前的值
|
||||
/// </summary>
|
||||
public static event Action<ConfigSelectableItem, object> OnAnyValueChange;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 静态成员
|
||||
|
||||
/// <summary>
|
||||
/// 所有有效的配置项目,当项目被隐藏(不处于激活状态时)就会从列表中删除。
|
||||
/// </summary>
|
||||
private static readonly List<ConfigSelectableItem> ConfigItemList = new List<ConfigSelectableItem>();
|
||||
|
||||
/// <summary>
|
||||
/// 项目列表有效成员的数量
|
||||
/// </summary>
|
||||
public static int ConfigItemListCount => ConfigItemList.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 获取
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static ConfigSelectableItem GetConfigItemByIndex(int index) => ConfigItemList[index];
|
||||
public static IEnumerable<ConfigSelectableItem> ConfigItemLists => ConfigItemList;
|
||||
|
||||
private static bool autoUpdateValueAll;
|
||||
/// <summary>
|
||||
/// 自动更新所有数值
|
||||
/// </summary>
|
||||
public static bool AutoUpdateValueAll
|
||||
{
|
||||
get => autoUpdateValueAll;
|
||||
set
|
||||
{
|
||||
foreach (var item in ConfigItemList)
|
||||
item.autoUpdateValue = value;
|
||||
autoUpdateValueAll = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 状态
|
||||
|
||||
/// <summary>
|
||||
/// 数值遭到修改时,标记脏,这样在后续的应用中有可以保存的变化
|
||||
/// <code>
|
||||
/// 建议:这是可选的
|
||||
/// 这会立刻发生在值修改时,且比OnValueChange更早,但在复位之前只会调用一次。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public event Action<ConfigSelectableItem> OnValueDirty;
|
||||
|
||||
/// <summary>
|
||||
/// 当数值收到修改时,产生事件,其中值的含义是修改前的值
|
||||
/// <code>
|
||||
/// 建议:这是可选的
|
||||
/// 这会立刻发生在值修改时,所以如果需要进行页面刷新的话,建议进行等待
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public event Action<ConfigSelectableItem, object> OnValueChange;
|
||||
|
||||
/// <summary>
|
||||
/// 当数值结束修改时
|
||||
/// </summary>
|
||||
public event Action<ConfigSelectableItem, object> OnEndEdit;
|
||||
/// <summary>
|
||||
/// 当前属性请求获取属性值
|
||||
/// <code>
|
||||
/// 建议:这是必要的
|
||||
/// 当发生刷新事件时,将产生调用。
|
||||
/// 含义是将外部的值设定到此。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public event Func<ConfigSelectableItem, object> GetSourceValue;
|
||||
|
||||
/// <summary>
|
||||
/// 当前属性请求设置属性值
|
||||
/// <code>
|
||||
/// 建议:这是必要的
|
||||
/// 当被应用时,比如调用了ForceSetAllSourceValue,将产生调用。
|
||||
/// 含义是将此处的界面值返回到属性中。
|
||||
/// 警告:不要在这里面调用ForceSetAllSourceValue,SetSourceValueRequest等方法,
|
||||
/// 这会造成死循环以及内存溢出等问题。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public event Action<ConfigSelectableItem, object> SetSourceValue;
|
||||
|
||||
/// <summary>
|
||||
/// 当前属性的自动更新请求获取
|
||||
/// <code>
|
||||
/// 建议:这是条件可选的
|
||||
/// 当被管理的目标中存在Toggle时(对应autoUpdateValueToggle项目)
|
||||
/// 此处将用于从外部的值是否勾选值设定到此
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public event Func<ConfigSelectableItem, bool> GetSourceValueAutoUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// 当前属性的自动更新请求设置
|
||||
/// <code>
|
||||
/// 建议:这是条件可选的
|
||||
/// 当被管理的目标中存在Toggle时(对应autoUpdateValueToggle项目)
|
||||
/// 此处将用于设置到外部的是否勾选值设定到此
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public event Action<bool> SetSourceValueAutoUpdate;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 请求此属性设置值(将值应用到寄存中)
|
||||
/// </summary>
|
||||
public void SetSourceValueRequest()
|
||||
{
|
||||
SetSourceValue?.Invoke(this, Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 属性字段
|
||||
|
||||
/// <summary>
|
||||
/// 项目对应的标记tag
|
||||
/// </summary>
|
||||
[Rename("映射标记")]
|
||||
public string configTag;
|
||||
|
||||
/// <summary>
|
||||
/// 设定是否有效。
|
||||
/// 如果无效,则不允许输入
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
[Rename("允许输入")]
|
||||
private bool allowInput = true;
|
||||
|
||||
/// <summary>
|
||||
/// 自动更新数值
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
[Rename("主动更新")]
|
||||
private bool autoUpdateValue = true;
|
||||
|
||||
/// <summary>
|
||||
/// 允许阻塞自动更新,表示此项目可以脱离自动更新状态选框的状态运行
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
[Rename("允许阻塞自动更新")]
|
||||
private bool allowBlockUpdate = true;
|
||||
|
||||
/// <summary>
|
||||
/// 阻塞自动更新,一般由程序自动根据焦点更新
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
[Rename("阻塞自动更新")]
|
||||
private bool forceBlockUpdate = false;
|
||||
|
||||
/// <summary>
|
||||
/// 格式化文本
|
||||
/// </summary>
|
||||
public string numberFormat = "0.##";
|
||||
|
||||
/// <summary>
|
||||
/// 项目标题组件
|
||||
/// </summary>
|
||||
public TextMeshProUGUI titleLabel;
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
#endif
|
||||
private string titleTextContext;
|
||||
|
||||
/// <summary>
|
||||
/// 帮助文本组件
|
||||
/// </summary>
|
||||
public TextMeshProUGUI helpLabel;
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
#endif
|
||||
private string helpTextContext;
|
||||
|
||||
/// <summary>
|
||||
/// 单位文本组件
|
||||
/// </summary>
|
||||
public TextMeshProUGUI unitLabel;
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
#endif
|
||||
private string unitTextContext;
|
||||
|
||||
/// <summary>
|
||||
/// 条目前的选框,与autoUpdateValue对应
|
||||
/// </summary>
|
||||
[Rename("自动更新状态选框")]
|
||||
public Toggle autoUpdateValueToggle;
|
||||
|
||||
/// <summary>
|
||||
/// 反转选框状态
|
||||
/// </summary>
|
||||
[Rename("反转自动更新按钮状态")]
|
||||
public bool autoUpdateValueToggleReversalState;
|
||||
|
||||
[Rename("反转自动更新状态")]
|
||||
public bool autoUpdateValueReversalState;
|
||||
|
||||
/// <summary>
|
||||
/// 脏属性自动复位。
|
||||
/// 当属性被重新开关时,将取消脏状态
|
||||
/// </summary>
|
||||
[Rename("脏标记自动复位")]
|
||||
public bool dirtyAutoReset = false;
|
||||
|
||||
// ==== 属性 ==== //
|
||||
|
||||
/// <summary>
|
||||
/// 设定是否输入有效
|
||||
/// </summary>
|
||||
public virtual bool AllowInput
|
||||
{
|
||||
get => allowInput;
|
||||
set => allowInput = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 此属性项目已经初始化
|
||||
/// </summary>
|
||||
[HideInInspector]
|
||||
public bool IsValueInitNull { get; private set; } = true;
|
||||
|
||||
// [Obsolete("请使用访问器")]
|
||||
private bool _isDirty;
|
||||
/// <summary>
|
||||
/// 脏属性标记,首次脏会进行广播
|
||||
/// </summary>
|
||||
private bool IsDirty
|
||||
{
|
||||
get => _isDirty;
|
||||
set
|
||||
{
|
||||
if (_isDirty)
|
||||
{
|
||||
if (!value)
|
||||
_isDirty = false;
|
||||
return;
|
||||
}
|
||||
if (!value) return;
|
||||
|
||||
_isDirty = true;
|
||||
OnAnyValueDirty?.Invoke(this);
|
||||
OnValueDirty?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设定自动更新状态,并同步选框
|
||||
/// <code>
|
||||
/// 为了书写方便,保存的状态是和外部暂存值一致,
|
||||
/// 此访问器将作为本地状态的标准访问。
|
||||
///
|
||||
/// 访问将自动进行转换,而设置则不进行转换,
|
||||
/// 所以应当使用此访问器进行设置或内部访问,而与外部通讯应当使用字段。
|
||||
///
|
||||
/// 如果需要将此内部判断状态转为界面状态:
|
||||
/// 应当先通过autoUpdateValueReversalState转为暂存值,
|
||||
/// 然后再通过autoUpdateValueToggleReversalState转为界面值。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public bool AutoUpdateValue
|
||||
{
|
||||
get => autoUpdateValueReversalState ^ autoUpdateValue;
|
||||
set
|
||||
{
|
||||
autoUpdateValue = value;
|
||||
if (autoUpdateValueToggle != null)
|
||||
autoUpdateValueToggle.isOn = autoUpdateValueToggleReversalState ^ value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设定阻塞自动更新
|
||||
/// </summary>
|
||||
public bool ForceBlockUpdate
|
||||
{
|
||||
get => forceBlockUpdate;
|
||||
set => forceBlockUpdate = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 项目标题
|
||||
/// </summary>
|
||||
public string TitleName
|
||||
{
|
||||
get => titleLabel.text;
|
||||
set => titleLabel.text = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 帮助文本,再默认情况下应该是不显示的通过赋予空文本来关闭显示
|
||||
/// </summary>
|
||||
public string HelpName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (helpLabel != null) return helpLabel.text;
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (helpLabel != null)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
helpLabel.text = value;
|
||||
helpLabel.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
helpLabel.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string UnitName
|
||||
{
|
||||
get => unitLabel.text;
|
||||
set => unitLabel.text = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
// [Obsolete("请使用访问器")]
|
||||
private object _value;
|
||||
|
||||
/// <summary>
|
||||
/// 项目值
|
||||
/// </summary>
|
||||
public object Value
|
||||
{
|
||||
get => _value;
|
||||
protected set
|
||||
{
|
||||
if (IsValueInitNull && value != null)
|
||||
IsValueInitNull = false;
|
||||
_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
// ==== 暂存 ==== //
|
||||
|
||||
public FieldInfo FieldInfo;
|
||||
|
||||
public Type FieldType;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
if (titleLabel != null)
|
||||
TitleName = titleTextContext;
|
||||
if (helpLabel != null)
|
||||
HelpName = helpTextContext;
|
||||
if (unitLabel != null)
|
||||
UnitName = unitTextContext;
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
foreach (var child in transform.GetChildren())
|
||||
{
|
||||
var component = child.GetComponent<UIBehaviour>();
|
||||
if (component != null)
|
||||
Initialization_ChildConstruction(component);
|
||||
}
|
||||
|
||||
Initialization_EventBinding();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
ConfigItemList.Add(this);
|
||||
ForceUpdateAllConfigEvent += ForceUpdate;
|
||||
ForceSetAllSourceValueEvent += SetSourceValueRequest;
|
||||
|
||||
if (dirtyAutoReset)
|
||||
IsDirty = false;
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
ConfigItemList.Remove(this);
|
||||
ForceUpdateAllConfigEvent -= ForceUpdate;
|
||||
ForceSetAllSourceValueEvent -= SetSourceValueRequest;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
ForceUpdate(true);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (IsValueInitNull || (AutoUpdateValue && !(allowBlockUpdate && forceBlockUpdate)))
|
||||
{
|
||||
ForceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ForceUpdateAllConfigEvent -= ForceUpdate;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 换算与转换
|
||||
|
||||
protected static double CastDigitalNumber(object newValue)
|
||||
{
|
||||
if (double.TryParse(newValue.ToString(), out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换到数值类型(数值钳制与四舍五入)
|
||||
/// </summary>
|
||||
/// <param name="newValue"></param>
|
||||
/// <param name="minValue"></param>
|
||||
/// <param name="maxValue"></param>
|
||||
/// <param name="digits"></param>
|
||||
/// <returns></returns>
|
||||
protected static double CastDigitalNumber(object newValue, double minValue, double maxValue, int digits)
|
||||
{
|
||||
// var resultValue = newValue switch
|
||||
// {
|
||||
// bool boolValue => boolValue ? 1 : 0,
|
||||
// short intValue => intValue,
|
||||
// int intValue => intValue,
|
||||
// long intValue => intValue,
|
||||
// float floatValue => Math.Round(floatValue, digits),
|
||||
// double doubleValue => Math.Round(doubleValue, digits),
|
||||
// _ => 0
|
||||
// };
|
||||
#if UNITY_EDITOR
|
||||
if (newValue == null)
|
||||
{
|
||||
Debug.LogError($"参数转换错误:目标是一个空值,请在发生空值时提前退出");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if (double.TryParse(newValue.ToString(), out var value))
|
||||
{
|
||||
var resultValue = Math.Round(value, digits);
|
||||
return Math.Clamp(resultValue, minValue, maxValue);
|
||||
}
|
||||
Debug.LogError($"参数转换错误:{newValue}可能不是一个有效的数值");
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected static int CastIntNumber(object newValue)
|
||||
{
|
||||
var resultValue = Convert.ToInt32(newValue);
|
||||
return resultValue;
|
||||
}
|
||||
|
||||
protected static Vector2 CastVector2(object newValue)
|
||||
{
|
||||
return newValue switch
|
||||
{
|
||||
Vector2 vec2Value => vec2Value,
|
||||
System.Numerics.Vector2 sysVec2Value => new Vector2(sysVec2Value.X, sysVec2Value.Y),
|
||||
_ => Vector2.zero
|
||||
};
|
||||
}
|
||||
|
||||
protected static Vector3 CastVector3(object newValue)
|
||||
{
|
||||
return newValue switch
|
||||
{
|
||||
Vector3 vec3Value => vec3Value,
|
||||
System.Numerics.Vector3 sysVec3Value => new Vector3(sysVec3Value.X, sysVec3Value.Y, sysVec3Value.Z),
|
||||
_ => Vector3.zero
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 初始化方法
|
||||
|
||||
/// <summary>
|
||||
/// 可选的基本初始化
|
||||
/// </summary>
|
||||
/// <param name="autoUpdateValue">设定自动更新</param>
|
||||
protected void Initialization(bool autoUpdateValue = true)
|
||||
{
|
||||
AutoUpdateValue = autoUpdateValue;
|
||||
}
|
||||
|
||||
private bool _doOnce = false;
|
||||
/// <summary>
|
||||
/// 构建属性,在初始化过程中,通过识别项目来构建域
|
||||
/// </summary>
|
||||
protected virtual void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
// 跟踪节点的名称
|
||||
var name = component.gameObject.name;
|
||||
// 初始化自动更新对勾
|
||||
if (name == "toggle" && component is Toggle toggle)
|
||||
{
|
||||
if (autoUpdateValueToggle == null)
|
||||
autoUpdateValueToggle = toggle;
|
||||
return;
|
||||
}
|
||||
if (name is not ("title" or "name")) return;
|
||||
if (_doOnce)
|
||||
{
|
||||
Debug.LogError("存在重复命名的属性模块:" + name);
|
||||
return;
|
||||
}
|
||||
if (component is TextMeshProUGUI textMeshProUGUI)
|
||||
{
|
||||
_doOnce = true;
|
||||
if (titleLabel != null)
|
||||
titleLabel = textMeshProUGUI;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建事件,在初始化结束前,需要对所有动态绑定的按键进行事件绑定
|
||||
/// </summary>
|
||||
protected virtual void Initialization_EventBinding()
|
||||
{
|
||||
// 无论如何,事件需要被注册
|
||||
if (autoUpdateValueToggle != null)
|
||||
{
|
||||
autoUpdateValueToggle.onValueChanged.AddListener(a =>
|
||||
{
|
||||
autoUpdateValue = autoUpdateValueToggleReversalState ^ a;
|
||||
// 访问器将会自动转换,所以使用字段
|
||||
SetSourceValueAutoUpdate?.Invoke(autoUpdateValue);
|
||||
// 可能不会脏
|
||||
// OnValueDirty?.Invoke(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 更新方法
|
||||
|
||||
/// <summary>
|
||||
/// 更新函数
|
||||
/// <code>
|
||||
/// 在其他任意时刻调用时,将强制刷新其中的功能,如果已经是脏属性则不会有任何行为。
|
||||
/// 更新是指从本地数据中更新,这会立刻产生值获取回调。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public virtual void ForceUpdate(bool resetDirty = false, bool forceSet = false)
|
||||
{
|
||||
if (resetDirty)
|
||||
IsDirty = false;
|
||||
var value = GetSourceValue?.Invoke(this);
|
||||
if (value == null)
|
||||
return ;
|
||||
RefreshValueWithoutEvent(value, forceSet);
|
||||
RefreshAutoUpdateValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置值,并触发更改事件,通常由界面发起所以不会主动更新界面
|
||||
/// </summary>
|
||||
/// <param name="newValue"></param>
|
||||
protected virtual void SetValue(object newValue)
|
||||
{
|
||||
if (Value == newValue)
|
||||
return;
|
||||
var lastValue = Value;
|
||||
// 如果不允许输入,将复位为现在的值
|
||||
if (!allowInput)
|
||||
RefreshValueWithoutEvent(Value);
|
||||
else
|
||||
{
|
||||
Value = newValue;
|
||||
IsDirty = true;
|
||||
|
||||
OnAnyValueChange?.Invoke(this, lastValue);
|
||||
OnValueChange?.Invoke(this, lastValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置值,不触发更改时的事件,但应该进行界面数值更新。
|
||||
/// </summary>
|
||||
/// <param name="newValue"></param>
|
||||
/// <param name="forceSet"></param>
|
||||
public virtual void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
if (!AutoUpdateValue && !forceSet)
|
||||
return;
|
||||
if (newValue == null)
|
||||
return;
|
||||
if (Value != null && (Value.ToString() == newValue.ToString()))
|
||||
return;
|
||||
Value = newValue;
|
||||
RefreshFormatValue(newValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新自动更新状态
|
||||
/// </summary>
|
||||
protected void RefreshAutoUpdateValue()
|
||||
{
|
||||
var isSourceValueAutoUpdate = GetSourceValueAutoUpdate?.Invoke(this);
|
||||
if (isSourceValueAutoUpdate != null)
|
||||
AutoUpdateValue = isSourceValueAutoUpdate.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新格式化文本,在触发刷新时将产生更新
|
||||
/// </summary>
|
||||
protected virtual void RefreshFormatValue(object newValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void WhenStartEdit()
|
||||
{
|
||||
// Debug.Log("开始编辑");
|
||||
ForceBlockUpdate = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数值输入完毕,或焦点移除时调用。
|
||||
/// </summary>
|
||||
protected void WhenEndEdit()
|
||||
{
|
||||
// Debug.Log("结束编辑");
|
||||
ForceBlockUpdate = false;
|
||||
OnEndEdit?.Invoke(this, Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 外观设定
|
||||
|
||||
/// <summary>
|
||||
/// 销毁自动更新复选框,并设定当前是否允许自动更新
|
||||
/// </summary>
|
||||
/// <param name="autoUpdateValue"></param>
|
||||
public void DestroyAutoUpdateValue(bool autoUpdateValue)
|
||||
{
|
||||
Destroy(autoUpdateValueToggle.gameObject);
|
||||
this.autoUpdateValue = autoUpdateValueReversalState ^ autoUpdateValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e3c729eeb8e03f459a9c95dc6e239d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
/// <summary>
|
||||
/// 一种普通文本输入框
|
||||
/// </summary>
|
||||
public class ConfigTextInputField : ConfigSelectableItem
|
||||
{
|
||||
#region 属性字段
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public string editorInput;
|
||||
#endif
|
||||
|
||||
public TMP_InputField targetInput;
|
||||
|
||||
|
||||
public override bool AllowInput
|
||||
{
|
||||
get => base.AllowInput;
|
||||
set
|
||||
{
|
||||
if (targetInput != null)
|
||||
targetInput.readOnly = !value;
|
||||
base.AllowInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
#if UNITY_EDITOR
|
||||
if (targetInput != null)
|
||||
targetInput.text = editorInput;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
targetInput.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInput.onValueChanged.AddListener(o =>
|
||||
{
|
||||
if(double.TryParse(o, out var value))
|
||||
SetValue(value);
|
||||
});
|
||||
targetInput.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实现
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
base.SetValue(newValue);
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(newValue, forceSet);
|
||||
}
|
||||
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
if (component is TMP_InputField inputField)
|
||||
{
|
||||
if (targetInput != null)
|
||||
targetInput = inputField;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RefreshFormatValue(object newValue)
|
||||
{
|
||||
base.RefreshFormatValue(newValue);
|
||||
targetInput.text = newValue.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9b27b4ed9920c947b157c1d7702372c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
/// <summary>
|
||||
/// 单选项控制组,将返回一个代表当前有效单选项索引作为值。
|
||||
/// </summary>
|
||||
public class ConfigToggleGroup : ConfigSelectableItem
|
||||
{
|
||||
#region 属性字段
|
||||
|
||||
public ToggleGroup targetToggle;
|
||||
|
||||
public List<Toggle> toggles;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
for (int i = 0; i < toggles.Count; i++)
|
||||
{
|
||||
void BuildInGroupInit()
|
||||
{
|
||||
var index = i;
|
||||
var toggle = toggles[i];
|
||||
toggle.onValueChanged.AddListener(a =>
|
||||
{
|
||||
if (a)
|
||||
SetValue(index);
|
||||
});
|
||||
}
|
||||
BuildInGroupInit();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实现
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
base.SetValue(CastIntNumber(newValue));
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(CastIntNumber(newValue), forceSet);
|
||||
}
|
||||
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
if (component is Toggle toggle &&
|
||||
!toggles.Contains(toggle))
|
||||
toggles.Add(toggle);
|
||||
}
|
||||
|
||||
protected override void RefreshFormatValue(object newValue)
|
||||
{
|
||||
base.RefreshFormatValue(newValue);
|
||||
var newIndex = CastIntNumber(newValue);
|
||||
// var index = 0;
|
||||
// foreach (var toggle in targetToggle.ActiveToggles())
|
||||
// {
|
||||
// if (newIndex != index++)
|
||||
// continue;
|
||||
// toggle.isOn = true;
|
||||
// Debug.Log(TitleName +":设置布尔项目");
|
||||
// }
|
||||
toggles[newIndex].SetIsOnWithoutNotify(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b1b61fd3a211734eb3d51cedcbe579f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class ConfigTypeOverrideAttribute : PropertyAttribute
|
||||
{
|
||||
public readonly string TypeName;
|
||||
|
||||
public ConfigTypeOverrideAttribute(string name)
|
||||
{
|
||||
TypeName = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取其中的rename定义名称
|
||||
/// </summary>
|
||||
/// <param name="field"></param>
|
||||
/// <param name="name">通过特性获取的名称,如果没有则为原生的名称</param>
|
||||
/// <returns>是否成功通过特性获取名称,否则是普通类型名称</returns>
|
||||
public static bool TryGetMemberName(FieldInfo field, out string name)
|
||||
{
|
||||
if (field.GetCustomAttribute(typeof(ConfigTypeOverrideAttribute)) is ConfigTypeOverrideAttribute rename)
|
||||
{
|
||||
name = rename.TypeName;
|
||||
return true;
|
||||
}
|
||||
|
||||
name = field.FieldType.Name;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a130623ae66ff564a9e14580549d2858
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,129 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
public class ConfigVec2InputField : ConfigSelectableItem
|
||||
{
|
||||
#region ÊôÐÔ×Ö¶Î
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public Vector2 editorInput;
|
||||
#endif
|
||||
|
||||
public TMP_InputField targetInputX;
|
||||
public TMP_InputField targetInputY;
|
||||
|
||||
public override bool AllowInput
|
||||
{
|
||||
get => base.AllowInput;
|
||||
set
|
||||
{
|
||||
if (targetInputX != null)
|
||||
targetInputX.readOnly = !value;
|
||||
if (targetInputY != null)
|
||||
targetInputY.readOnly = value;
|
||||
base.AllowInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ÉúÃüÖÜÆÚ
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
#if UNITY_EDITOR
|
||||
if (targetInputX != null)
|
||||
targetInputX.text = editorInput.x.ToString();
|
||||
if (targetInputY != null)
|
||||
targetInputY.text = editorInput.y.ToString();
|
||||
#endif
|
||||
}
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
targetInputX.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInputY.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInputX.onValueChanged.AddListener(a =>
|
||||
{
|
||||
var value = CastVector3(Value);
|
||||
value.x = float.Parse(a);
|
||||
SetValue(value);
|
||||
});
|
||||
targetInputY.onValueChanged.AddListener(a =>
|
||||
{
|
||||
var value = CastVector3(Value);
|
||||
value.y = float.Parse(a);
|
||||
SetValue(value);
|
||||
});
|
||||
targetInputX.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
targetInputY.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ʵÏÖ
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
base.SetValue(CastVector2(newValue));
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(CastVector2(newValue), forceSet);
|
||||
}
|
||||
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
var name = component.transform.name;
|
||||
if (component is not TMP_InputField inputField)
|
||||
return;
|
||||
switch (name)
|
||||
{
|
||||
case "inputx":
|
||||
case "inputX":
|
||||
if (targetInputX != null)
|
||||
targetInputX = inputField;
|
||||
break;
|
||||
case "inputy":
|
||||
case "inputY":
|
||||
if (targetInputY != null)
|
||||
targetInputY = inputField;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RefreshFormatValue(object newValue)
|
||||
{
|
||||
base.RefreshFormatValue(newValue);
|
||||
var value = CastVector2(newValue);
|
||||
targetInputX.text = value.x.ToString(numberFormat);
|
||||
targetInputY.text = value.y.ToString(numberFormat);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 118ff8f18137da54087c6d572fc5db75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace LRC
|
||||
{
|
||||
|
||||
public class ConfigVec3InputField : ConfigSelectableItem
|
||||
{
|
||||
#region ÊôÐÔ×Ö¶Î
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public Vector3 editorInput;
|
||||
#endif
|
||||
|
||||
public TMP_InputField targetInputX;
|
||||
public TMP_InputField targetInputY;
|
||||
public TMP_InputField targetInputZ;
|
||||
|
||||
public override bool AllowInput
|
||||
{
|
||||
get => base.AllowInput;
|
||||
set
|
||||
{
|
||||
if (targetInputX != null)
|
||||
targetInputX.readOnly = !value;
|
||||
if (targetInputY != null)
|
||||
targetInputY.readOnly = value;
|
||||
if (targetInputZ != null)
|
||||
targetInputZ.readOnly = value;
|
||||
base.AllowInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ÉúÃüÖÜÆÚ
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
#if UNITY_EDITOR
|
||||
if (targetInputX != null)
|
||||
targetInputX.text = editorInput.x.ToString();
|
||||
if (targetInputY != null)
|
||||
targetInputY.text = editorInput.y.ToString();
|
||||
if (targetInputZ != null)
|
||||
targetInputZ.text = editorInput.z.ToString();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
targetInputX.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInputY.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInputZ.onSelect.AddListener(a =>
|
||||
{
|
||||
WhenStartEdit();
|
||||
});
|
||||
targetInputX.onValueChanged.AddListener(a =>
|
||||
{
|
||||
var value = CastVector3(Value);
|
||||
value.x = float.Parse(a);
|
||||
SetValue(value);
|
||||
});
|
||||
targetInputY.onValueChanged.AddListener(a =>
|
||||
{
|
||||
var value = CastVector3(Value);
|
||||
value.y = float.Parse(a);
|
||||
SetValue(value);
|
||||
});
|
||||
targetInputZ.onValueChanged.AddListener(a =>
|
||||
{
|
||||
var value = CastVector3(Value);
|
||||
value.z = float.Parse(a);
|
||||
SetValue(value);
|
||||
});
|
||||
targetInputX.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
targetInputY.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
targetInputZ.onEndEdit.AddListener(delegate
|
||||
{
|
||||
WhenEndEdit();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ʵÏÖ
|
||||
|
||||
protected override void SetValue(object newValue)
|
||||
{
|
||||
base.SetValue(CastVector3(newValue));
|
||||
}
|
||||
|
||||
public override void RefreshValueWithoutEvent(object newValue, bool forceSet = false)
|
||||
{
|
||||
base.RefreshValueWithoutEvent(CastVector3(newValue), forceSet);
|
||||
}
|
||||
|
||||
protected override void Initialization_ChildConstruction(UIBehaviour component)
|
||||
{
|
||||
base.Initialization_ChildConstruction(component);
|
||||
|
||||
var name = component.transform.name;
|
||||
if (component is not TMP_InputField inputField)
|
||||
return;
|
||||
switch (name)
|
||||
{
|
||||
case "inputx":
|
||||
case "inputX":
|
||||
if (targetInputX != null)
|
||||
targetInputX = inputField;
|
||||
break;
|
||||
case "inputy":
|
||||
case "inputY":
|
||||
if (targetInputY != null)
|
||||
targetInputY = inputField;
|
||||
break;
|
||||
case "inputz":
|
||||
case "inputZ":
|
||||
if (targetInputZ != null)
|
||||
targetInputZ = inputField;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RefreshFormatValue(object newValue)
|
||||
{
|
||||
base.RefreshFormatValue(newValue);
|
||||
var value = CastVector3(newValue);
|
||||
targetInputX.text = value.x.ToString(numberFormat);
|
||||
targetInputY.text = value.y.ToString(numberFormat);
|
||||
targetInputZ.text = value.z.ToString(numberFormat);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6e1779348dcc2b48a6f009b828e9b5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8777f3ce94f12747a9c712730114c5b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,365 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Deconstruction.Element;
|
||||
using Deconstruction.Interface;
|
||||
using Deconstruction.Manager;
|
||||
using Deconstruction.Tool;
|
||||
using Deconstruction.Trajectory;
|
||||
using Deconstruction.Type.DMToolSlot;
|
||||
using Deconstruction.Type.Linkable;
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
using LineSegment2 = Deconstruction.Element.PromptLine2.LineSegment2;
|
||||
|
||||
/// <summary>
|
||||
/// 绘制轨迹线的操作
|
||||
/// <code>
|
||||
/// 当从其他WaitForUserInput_OpSlot到达此处时,将创建TerminalLine2T3用于线路的绘制,
|
||||
/// 同时在线路的两端有两个TerminalPoint用于线路的相连。
|
||||
///
|
||||
/// 支持常规的线路连续绘制,起点吸附,终点吸附。
|
||||
/// 不支持首尾相连。
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public class DrawTerminalLine2T3_OpSlot : WaitForUserInput_OpSlot
|
||||
{
|
||||
#region 字段属性
|
||||
|
||||
/// <summary>
|
||||
/// 上一步的坐标
|
||||
/// </summary>
|
||||
private Vector3 _lastPointPosition;
|
||||
|
||||
/// <summary>
|
||||
/// 上一步的对齐轨迹计算器
|
||||
/// </summary>
|
||||
private Vector3 _lastPointNormal;
|
||||
|
||||
/// <summary>
|
||||
/// 下一步的坐标
|
||||
/// </summary>
|
||||
private Vector3 _helperPosition = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// 下一步的法向
|
||||
/// </summary>
|
||||
private Vector3 _helperNormal = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// 操作的轨迹线
|
||||
/// </summary>
|
||||
private TerminalLine2T3 _line;
|
||||
|
||||
/// <summary>
|
||||
/// 起点
|
||||
/// </summary>
|
||||
private TerminalPoint _startPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 操作的点,也是终点
|
||||
/// </summary>
|
||||
private TerminalPoint _endPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 在场上找到的其他的点
|
||||
/// </summary>
|
||||
private TerminalPoint _otherPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 起点是用上一步的终点替代的
|
||||
/// </summary>
|
||||
private bool _isStartPointSubstitute = false;
|
||||
|
||||
/// <summary>
|
||||
/// 绘制时,终点是自由点
|
||||
/// </summary>
|
||||
private bool _isFreeEndPoint;
|
||||
|
||||
// public SesothoArrangementWiresTool myWiresTool;
|
||||
// myWiresTool = targetTool as SesothoArrangementWiresTool;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 引用封装
|
||||
|
||||
/// <summary>
|
||||
/// 创建自己的工具
|
||||
/// </summary>
|
||||
private SesothoArrangementWiresTool selfWiresTool => SesothoPeManager.WiresTool;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
// 检查抵达此处的流程是否正确
|
||||
if (!HasLastNode)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"绘制节点行为树顺序错误,必须从其他任意WaitForUserInput_OpSlot中抵达此处");
|
||||
RemoveThis();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Last is not WaitForUserInput_OpSlot)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"绘制节点行为树顺序错误,应该是 WaitForUserInput_OpSlot -> {nameof(DrawTerminalLine2T3_OpSlot)},而不是{Last.GetType().Name}");
|
||||
RemoveThis();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置先决条件
|
||||
InitPrecondition();
|
||||
}
|
||||
|
||||
protected override void OnEnd()
|
||||
{
|
||||
base.OnEnd();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// 阻止基类的默认操作
|
||||
// base.Update();
|
||||
|
||||
// 线的绘制计算,包括空间点吸附,链接等
|
||||
DrawingProcess();
|
||||
// 计算完毕后执行渲染,这里调用有可能会带来空引用问题
|
||||
// 但在此之前的阶段已经对此进行过了处理所以没有关系,如果要在其他地方照抄的话需要注意。
|
||||
_line.UpdateRender();
|
||||
|
||||
// 下一步
|
||||
if (IsReleaseContinueKey())
|
||||
{
|
||||
_helperNormal = _line.TrajectoryCalculator.GetEndPointNormal.UpwardPlaneToVector3();
|
||||
|
||||
// 起点链接
|
||||
if (_isStartPointSubstitute)
|
||||
{
|
||||
// 在开始时已经做过转换了
|
||||
// var dtlt_Slot = Last as DrawTerminalLine2T3_OpSlot;
|
||||
_line.LinkedDataAddPolarity(_startPoint, LinkType.Import);
|
||||
}
|
||||
else
|
||||
{
|
||||
_line.LinkedDataAddPolarity(_startPoint, LinkType.Import);
|
||||
selfWiresTool.PersistentElement(_startPoint);
|
||||
selfWiresTool.AddPeToNeighborGrid(_startPoint);
|
||||
}
|
||||
|
||||
// 终点链接
|
||||
if (_otherPoint is null) // || _endPoint.gameObject.activeSelf
|
||||
{
|
||||
_line.LinkedDataAddPolarity(_endPoint, LinkType.Export);
|
||||
selfWiresTool.PersistentElement(_endPoint);
|
||||
selfWiresTool.AddPeToNeighborGrid(_endPoint);
|
||||
}
|
||||
// 如果存在吸附目标
|
||||
else
|
||||
{
|
||||
selfWiresTool.DeleteElement(_endPoint);
|
||||
_line.LinkedDataAddPolarity(_otherPoint, LinkType.Export);
|
||||
}
|
||||
|
||||
// 保存线
|
||||
_line.ReapplyOrigin();
|
||||
selfWiresTool.PersistentElement(_line);
|
||||
selfWiresTool.AddBulkPeToNeighborGrid(_line);
|
||||
|
||||
// 下一步
|
||||
ReplaceThis(new DrawTerminalLine2T3_OpSlot());
|
||||
}
|
||||
// 取消
|
||||
else if (IsReleaseCanelKey())
|
||||
{
|
||||
RemoveThis();
|
||||
EndPrecondition();
|
||||
}
|
||||
// todo: 回到上一步
|
||||
/*
|
||||
* 操作栈的结构使得操作与对象的更新可以分离,
|
||||
* 就是撤回的过程可能有点复杂,
|
||||
* 需要分为工具运行期间撤回和常规撤回。
|
||||
*/
|
||||
}
|
||||
|
||||
protected override void AsyncBeforeUpdate()
|
||||
{
|
||||
base.AsyncBeforeUpdate();
|
||||
}
|
||||
|
||||
protected override void AsyncUpdate()
|
||||
{
|
||||
base.AsyncUpdate();
|
||||
}
|
||||
|
||||
protected override void AsyncAfterUpdate()
|
||||
{
|
||||
base.AsyncAfterUpdate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
|
||||
/// <summary>
|
||||
/// 设置先决属性
|
||||
/// </summary>
|
||||
private void InitPrecondition()
|
||||
{
|
||||
switch (Last)
|
||||
{
|
||||
// 使用来自前者的元素
|
||||
case DrawTerminalLine2T3_OpSlot dtlt_Slot:
|
||||
_isStartPointSubstitute = true;
|
||||
_startPoint = dtlt_Slot._endPoint;
|
||||
_lastPointPosition = dtlt_Slot._helperPosition;
|
||||
_lastPointNormal = dtlt_Slot._helperNormal;
|
||||
break;
|
||||
// 新建,或从空间中继承
|
||||
case WaitForUserInput_OpSlot wfui_Slot:
|
||||
if (SesothoArrangementWiresTool.GetNearestObjectAsPoint(out var terminalPoint))
|
||||
{
|
||||
_isStartPointSubstitute = true;
|
||||
_startPoint = terminalPoint;
|
||||
_lastPointPosition = terminalPoint.GetPosition3();
|
||||
if (SesothoArrangementWiresTool.GetNearestObjectAsLine(terminalPoint, out var terminalLine,
|
||||
out var normal))
|
||||
_lastPointNormal = normal;
|
||||
else
|
||||
{
|
||||
Debug.LogError("错误:当前绘制的线路准备吸附的目标并非一个完整的链路,或类型错误,正在退出。");
|
||||
RemoveThis();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_isStartPointSubstitute = false;
|
||||
selfWiresTool.GetElement(out _startPoint);
|
||||
_lastPointPosition = SesothoArrangementWiresTool.OpKey_IgnoreNeighborAdsorption.Getkey() ?
|
||||
wfui_Slot.MouseHelperPosition :
|
||||
CameraMouseInputHelper.Inst.GetGridAdsorb(wfui_Slot.MouseHelperPosition);
|
||||
_lastPointNormal = default;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
selfWiresTool.GetElement(out _line);
|
||||
selfWiresTool.GetElement(out _endPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束清理属性
|
||||
/// </summary>
|
||||
private void EndPrecondition()
|
||||
{
|
||||
selfWiresTool.DeleteElement(_line);
|
||||
// 不能把别人的东西给回收了
|
||||
if (!_isStartPointSubstitute)
|
||||
selfWiresTool.DeleteElement(_startPoint);
|
||||
selfWiresTool.DeleteElement(_endPoint);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 线路绘制过程
|
||||
/// </summary>
|
||||
private void DrawingProcess()
|
||||
{
|
||||
// 获取当前的坐标
|
||||
_helperPosition = PlaceElementManager.Inst.InputHelper.CurrentGridPosition;
|
||||
|
||||
_startPoint.transform.position = _lastPointPosition;
|
||||
|
||||
_isFreeEndPoint = true;
|
||||
// 如果吸附到任意物体,或者用按键忽略这一过程
|
||||
if (SesothoPeManager.NearestObject is not null &&
|
||||
SesothoArrangementWiresTool.GetNearestObjectAsPoint(out _otherPoint) &&
|
||||
!SesothoArrangementWiresTool.OpKey_IgnoreNeighborAdsorption.Getkey())
|
||||
{
|
||||
_isFreeEndPoint = false;
|
||||
if (_endPoint.gameObject.activeSelf)
|
||||
_endPoint.gameObject.SetActive(false);
|
||||
|
||||
_helperPosition = SesothoPeManager.NearestPoint;
|
||||
_endPoint.transform.position = SesothoPeManager.NearestPoint;
|
||||
if (SesothoArrangementWiresTool.GetNearestObjectAsLine(_otherPoint, out var terminalLine,
|
||||
out var normal))
|
||||
_helperNormal = normal;
|
||||
}
|
||||
// 如果吸附到任意切线
|
||||
// else if (NearestLineTangentObject != null)
|
||||
// {
|
||||
// NearestLineTangentPoint
|
||||
// }
|
||||
|
||||
// 否则自由点
|
||||
if (_isFreeEndPoint)
|
||||
{
|
||||
if (!_endPoint.gameObject.activeSelf)
|
||||
_endPoint.gameObject.SetActive(true);
|
||||
_otherPoint = null;
|
||||
_helperNormal = default;
|
||||
_endPoint.transform.position = _helperPosition;
|
||||
}
|
||||
|
||||
_line.SetLineSegment(
|
||||
_lastPointPosition, _helperPosition,
|
||||
_lastPointNormal, _helperNormal
|
||||
);
|
||||
|
||||
// 如果当前线的线计算器是圆弧计算器,那么获取它的角度
|
||||
// if (_line.NowCalculator is RoundTrajectory2 arc)
|
||||
// {
|
||||
// Debug.Log($"角度{arc.Angle} {arc.AngleExact}");
|
||||
// }
|
||||
|
||||
var forceStr = SesothoArrangementWiresTool.OpKey_ForceStr.Getkey();
|
||||
if (forceStr)
|
||||
_line.DrawStraightLine();
|
||||
else
|
||||
{
|
||||
var force90Arc = SesothoArrangementWiresTool.OpKey_90Arc.Getkey();
|
||||
var force180Arc = SesothoArrangementWiresTool.OpKey_180Arc.Getkey();
|
||||
var forceRevCisoid = SesothoArrangementWiresTool.OpKey_RevCisoid.Getkey();
|
||||
|
||||
|
||||
var angle = -1;
|
||||
if (force90Arc || force180Arc)
|
||||
{
|
||||
angle = 0;
|
||||
if (force90Arc)
|
||||
angle += 90;
|
||||
if (force180Arc)
|
||||
angle += 180;
|
||||
}
|
||||
|
||||
_line.AutomaticDrawCircularLine(angle, forceRevCisoid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取光标下的起点
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool GetCurrentObject()
|
||||
{
|
||||
// SesothoPeManager.
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d957a4fb102b5a649b52cd9f76b57f93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bce0f9a72c71a9a4c9e79c61ee4a4bf8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
/// <summary>
|
||||
/// 线路绘制编辑器
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(SesothoPeManager))]
|
||||
public class SesothoPeManagerEditor : Editor
|
||||
{
|
||||
#region 委托事件
|
||||
|
||||
/// <summary>
|
||||
/// 当前主要的鼠标动作
|
||||
/// </summary>
|
||||
private event Action CurrenMouseAction;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 字段属性
|
||||
|
||||
/// <summary>
|
||||
/// 是编辑绘制模式
|
||||
/// </summary>
|
||||
public bool IsDebugDrawMode;
|
||||
|
||||
private Vector3 _currentPosition;
|
||||
|
||||
private bool OpKey_ForceStr;
|
||||
private bool OpKey_90Arc;
|
||||
private bool OpKey_180Arc;
|
||||
private bool OpKey_RevCisoid;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
// private void OnEnable()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
//
|
||||
// private void OnDisable()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
private void OnSceneGUI()
|
||||
{
|
||||
if (!IsDebugDrawMode) return;
|
||||
|
||||
int controlID = GUIUtility.GetControlID(FocusType.Keyboard);
|
||||
if(GUIUtility.hotControl == 0)
|
||||
HandleUtility.AddDefaultControl(controlID);
|
||||
|
||||
OpKey_ForceStr = false;
|
||||
OpKey_90Arc = false;
|
||||
OpKey_180Arc = false;
|
||||
OpKey_RevCisoid = false;
|
||||
|
||||
if (Event.current.GetTypeForControl(controlID) == EventType.KeyDown)
|
||||
{
|
||||
switch (Event.current.keyCode)
|
||||
{
|
||||
case KeyCode.Escape:
|
||||
IsDebugDrawMode = false;
|
||||
break;
|
||||
case KeyCode.LeftControl:
|
||||
OpKey_ForceStr = true;
|
||||
break;
|
||||
case KeyCode.LeftAlt:
|
||||
OpKey_90Arc = true;
|
||||
break;
|
||||
case KeyCode.LeftShift:
|
||||
OpKey_180Arc = true;
|
||||
break;
|
||||
case KeyCode.R:
|
||||
OpKey_RevCisoid = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var mousePos = Event.current.mousePosition;
|
||||
if (Physics.Raycast(HandleUtility.GUIPointToWorldRay(mousePos), out var hit, float.MaxValue, 0xffff))
|
||||
{
|
||||
_currentPosition = hit.point;
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.MouseUp && GUIUtility.hotControl == 0)
|
||||
{
|
||||
if (Event.current.button == 0)
|
||||
{
|
||||
CurrenMouseAction?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (GUILayout.Button(IsDebugDrawMode ? "退出绘制模式" : "进入绘制模式"))
|
||||
IsDebugDrawMode = !IsDebugDrawMode;
|
||||
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 画线
|
||||
|
||||
private TerminalLine2T3 _line;
|
||||
private TerminalPoint _startPoint;
|
||||
private TerminalPoint _endPoint;
|
||||
|
||||
private void DarwLine()
|
||||
{
|
||||
_endPoint.transform.position = _currentPosition;
|
||||
|
||||
// _line.SetLineSegment(
|
||||
// _lastPointPosition, _currentPosition,
|
||||
// _lastPointNormal, _helperNormal
|
||||
// );
|
||||
|
||||
|
||||
if (OpKey_ForceStr)
|
||||
_line.DrawStraightLine();
|
||||
else
|
||||
{
|
||||
var angle = -1;
|
||||
if (OpKey_90Arc || OpKey_180Arc)
|
||||
{
|
||||
angle = 0;
|
||||
if (OpKey_90Arc)
|
||||
angle += 90;
|
||||
if (OpKey_180Arc)
|
||||
angle += 180;
|
||||
}
|
||||
_line.AutomaticDrawCircularLine(angle, OpKey_RevCisoid);
|
||||
}
|
||||
|
||||
// 计算完毕后执行渲染,这里调用有可能会带来空引用问题
|
||||
// 但在此之前的阶段已经对此进行过了处理所以没有关系,如果要照抄的话需要进行一些修改。
|
||||
_line.UpdateRender();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e2d9409c1fef764fa5d423dda5b3cfe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
/// <summary>
|
||||
/// 可链接的对象,表示此成员内包含一个链表的节点,可用于链表跟踪
|
||||
/// </summary>
|
||||
public interface ILinkconfident<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取链接点
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
public bool GetLinkedNode(out LinkedListNode<T> node);
|
||||
/// <summary>
|
||||
/// 设置连接点
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
public void SetLinkedNode(LinkedListNode<T> node);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99f791f3c433086449c6544bc265841d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Deconstruction.Element;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
/// <summary>
|
||||
/// 可链接的对象,表示此成员内包含一个链表的节点,可用于链表跟踪
|
||||
/// </summary>
|
||||
public interface ILinkconfidentPe : ILinkconfident<PlacementBase>
|
||||
{ }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9723e804f97bc024c8e1e1d6f8bc80a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,558 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de244510f627b4846a80a7402e13d3de
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,187 @@
|
||||
#define _DEBUG_
|
||||
// #undef _DEBUG_
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Deconstruction.Element;
|
||||
using UnityEngine;
|
||||
|
||||
using Deconstruction.Manager;
|
||||
using Deconstruction.Tool;
|
||||
using Deconstruction.Type.Area;
|
||||
using Deconstruction.Type.Serialize;
|
||||
using Newtonsoft.Json;
|
||||
using SerializerHelper.Type;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.Debuger;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
/// <summary>
|
||||
/// 主要管理器
|
||||
/// <code>
|
||||
/// 不建议继续封装管理器类
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public sealed class SesothoPeManager : PlaceElementManager
|
||||
{
|
||||
#region 静态成员
|
||||
|
||||
/// <summary>
|
||||
/// 此处提供的单例与父类单例是独立的
|
||||
/// </summary>
|
||||
public new static SesothoPeManager Inst => _inst;
|
||||
private static SesothoPeManager _inst;
|
||||
|
||||
/// <summary>
|
||||
/// 线路绘制工具
|
||||
/// </summary>
|
||||
public static SesothoArrangementWiresTool WiresTool => _wiresTool;
|
||||
private static SesothoArrangementWiresTool _wiresTool;
|
||||
|
||||
/// <summary>
|
||||
/// 光标下最近的线上交点
|
||||
/// </summary>
|
||||
public static Vector3 NearestLineTangentPoint => _lineTangentTraceTool.NearsetPoint;
|
||||
/// <summary>
|
||||
/// 光标下最近的线
|
||||
/// </summary>
|
||||
public static PlacementBase NearestLineTangentObject => _lineTangentTraceTool.NearsetObject;
|
||||
/// <summary>
|
||||
/// 光标下最近的对象原点
|
||||
/// </summary>
|
||||
public static Vector3 NearestPoint => TrackingTool.TrackingCurrentPosition;
|
||||
/// <summary>
|
||||
/// 光标下最近的对象
|
||||
/// </summary>
|
||||
public static PlacementBase NearestObject => TrackingTool.TrackingCurrentTarget;
|
||||
|
||||
private static TrajectoryTrackingTool.TrackPersistentCommunicate _lineTangentTraceTool;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 调试功能
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
public bool EnableGridDebugDraw = false;
|
||||
|
||||
|
||||
public void DebugFunc()
|
||||
{
|
||||
if (EnableGridDebugDraw)
|
||||
{
|
||||
PlacementNeighbor.DebugDrawGrid();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
_inst = this;
|
||||
|
||||
if (LineMaterial == null)
|
||||
Debug.LogError("需要给管理器提供一个线段材质");
|
||||
if (PointMaterial == null)
|
||||
Debug.LogError("需要给管理器提供一个点材质");
|
||||
|
||||
// 初始化画线工具
|
||||
_wiresTool = new SesothoArrangementWiresTool();
|
||||
_wiresTool.InitializeParent(transform);
|
||||
|
||||
_lineTangentTraceTool = TrackingTool.InstantiationTrackCaculate(PlaceholdersAreaType.LinearContinuity);
|
||||
|
||||
// 手动开启代理更新
|
||||
EnableUpdate = true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
}
|
||||
|
||||
private NeighborGrid<PlacementBase>.NeighborGridIndex index;
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
#if UNITY_EDITOR
|
||||
DebugFunc();
|
||||
#endif
|
||||
if (Input.GetMouseButtonDown(2))
|
||||
{
|
||||
index = PlacementNeighbor.GetNeighborIndex(null);
|
||||
index.SetDrivenAsWorld(InputHelper.CurrentPosition);
|
||||
Debug.Log($"{index.GetDrivenAsLinear()} => {index.GetNeighbor().FirstOrDefault()}");
|
||||
}
|
||||
|
||||
// new SerializeUnion();
|
||||
}
|
||||
|
||||
private string serializeContext = null;
|
||||
protected override void LateUpdate()
|
||||
{
|
||||
base.LateUpdate();
|
||||
// if (DecisionMakerToolBase.ActiveDecisionMaker == null ||
|
||||
// !DecisionMakerToolBase.ActiveDecisionMaker.EnableTool)
|
||||
#if !UNITY_EDITOR || !_DEBUG_
|
||||
return;
|
||||
#endif
|
||||
// 工具功能测试
|
||||
if (Input.GetKeyDown(KeyCode.L))
|
||||
{
|
||||
_wiresTool.EnableTool = !_wiresTool.EnableTool;
|
||||
Debug.Log($"{(_wiresTool.EnableTool ? "绘制工具已激活" : "工具已取消激活")}");
|
||||
}
|
||||
|
||||
// 存档功能测试
|
||||
if (Input.GetKeyDown(KeyCode.S))
|
||||
{
|
||||
var obj = _wiresTool.ToolSerializeDispost();
|
||||
serializeContext = JsonConvert.SerializeObject(obj);
|
||||
Debug.Log(obj.ToString());
|
||||
}
|
||||
if (Input.GetKeyDown(KeyCode.D))
|
||||
{
|
||||
if (serializeContext == null)
|
||||
{
|
||||
Debug.Log("反序列化内容为空");
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = JsonConvert.DeserializeObject<SerializeUnion>(serializeContext);
|
||||
_wiresTool.ToolDiserializDispost(obj);
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.A))
|
||||
{
|
||||
_wiresTool.EnableCompress = !_wiresTool.EnableCompress;
|
||||
if (_wiresTool.EnableCompress )
|
||||
Debug.Log("开启压缩");
|
||||
else
|
||||
Debug.Log("关闭压缩");
|
||||
}
|
||||
|
||||
|
||||
// 绘制一下吸附的目标位置
|
||||
if (TrackingTool.EnableTool || TrackingTool._enableAuxiliaryTool)
|
||||
{
|
||||
MacroDebugDraw.DrawDownArrow(NearestPoint, Quaternion.identity, Color.magenta);
|
||||
MacroDebugDraw.DrawDownArrow(NearestLineTangentPoint, Quaternion.identity, Color.green);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7825d9ef51f777c48aa66f3c83640259
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using Deconstruction.Element;
|
||||
using Deconstruction.Interface;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using XericLibrary.Runtime.Debuger;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
using XericLibrary.Runtime.Type;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
|
||||
using LineSegment2 = Deconstruction.Element.PromptLine2.LineSegment2;
|
||||
|
||||
/// <summary>
|
||||
/// 线终端执行脚本,使用二维线计算器,驱动三维线渲染器。
|
||||
/// </summary>
|
||||
public class TerminalLine2T3 : PromptLine2,
|
||||
ILinkconfidentPe // 链表跟踪
|
||||
{
|
||||
#region 字段属性
|
||||
|
||||
private LinkedListNode<PlacementBase> _linkNode;
|
||||
|
||||
/// <summary>
|
||||
/// 本地缓存
|
||||
/// </summary>
|
||||
private LineSegment2 _lineSegment = new LineSegment2();
|
||||
|
||||
public bool DebugDrawGrid = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
InitPromptLine<LineRendererUnbodied>();
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
SesothoPeManager.Inst.AddEmbedded(this);
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
SesothoPeManager.Inst.RemoveEmbedded(this);
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void AgencyOnEnable()
|
||||
{
|
||||
base.AgencyOnEnable();
|
||||
}
|
||||
|
||||
protected override void AgencyOnDisable()
|
||||
{
|
||||
base.AgencyOnDisable();
|
||||
}
|
||||
|
||||
protected override void AgencyMainUpdate()
|
||||
{
|
||||
base.AgencyMainUpdate();
|
||||
|
||||
// 绘制线路本身在网格中的索引位置
|
||||
if (DebugDrawGrid &&
|
||||
NeighborGridIndex.SafeGetAsMappingIndex(out var indexs))
|
||||
{
|
||||
foreach (var index in indexs.Indexs)
|
||||
{
|
||||
MacroDebugDraw.DrawDownArrow(
|
||||
index.GetCellWorldIndexPosition() + MacroMath.RandomVector3(0.1f, Identifier), Quaternion.identity,
|
||||
Color.red);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void AgencyAsyncBeforeUpdate()
|
||||
{
|
||||
base.AgencyAsyncBeforeUpdate();
|
||||
}
|
||||
|
||||
protected override void AgencyAsyncUpdate()
|
||||
{
|
||||
base.AgencyAsyncUpdate();
|
||||
}
|
||||
|
||||
protected override void AgencyAsyncAfterUpdate()
|
||||
{
|
||||
base.AgencyAsyncAfterUpdate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 线路构建
|
||||
|
||||
/// <summary>
|
||||
/// 创建合理坐标空间
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="ah"></param>
|
||||
/// <param name="bh"></param>
|
||||
/// <returns></returns>
|
||||
public LineSegment2 SetLineSegment(Vector3 a, Vector3 b, Vector3 ah = default, Vector3 bh = default)
|
||||
{
|
||||
_lineSegment.SetValue(
|
||||
a.UpwardPlaneToVector2(),
|
||||
b.UpwardPlaneToVector2(),
|
||||
ah.UpwardPlaneToVector2(),
|
||||
bh.UpwardPlaneToVector2());
|
||||
// ah?.GetEndPointNormal ?? default,
|
||||
// bh?.GetStartPointNormal ?? default);
|
||||
return _lineSegment;
|
||||
}
|
||||
|
||||
public void AutomaticDrawCircularLine(float constraintAngle = -1, bool fullArc = false)
|
||||
{
|
||||
AutomaticDrawCircularLine(_lineSegment, constraintAngle, fullArc);
|
||||
}
|
||||
|
||||
private LineSegment2 straightLine = new LineSegment2();
|
||||
public void DrawStraightLine()
|
||||
{
|
||||
straightLine.SetValue(_lineSegment.pointA, _lineSegment.pointB);
|
||||
AutomaticDrawCircularLine(straightLine);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 重新计算原点
|
||||
/// <code>
|
||||
/// 如果此轨迹的原点在零点,那么通过此方法可以将原点设置到起点和终点之间。
|
||||
/// 注意:使用前需要将轨迹设为本地坐标
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public void ReapplyOrigin()
|
||||
{
|
||||
if (TrajectoryRenderer == null || TrajectoryCalculator == null)
|
||||
{
|
||||
Debug.LogError("当前轨迹或线渲染器无效,无法计算原点");
|
||||
return;
|
||||
}
|
||||
|
||||
// 假定其中的轨迹是本地坐标的
|
||||
|
||||
// 期望的本地零点
|
||||
var center = ((TrajectoryCalculator.GetStartPointPosition + TrajectoryCalculator.GetEndPointPosition) / 2).UpwardPlaneToVector3();
|
||||
// 本地原点
|
||||
var local = TrajectoryCalculator.Origin.UpwardPlaneToVector3();
|
||||
// 期望偏移
|
||||
var offset = local - center;
|
||||
|
||||
TrajectoryCalculator.Origin = offset.UpwardPlaneToVector2();
|
||||
transform.position += center;
|
||||
|
||||
// 更新
|
||||
UpdateRender();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实现 - ILinkconfidentPe
|
||||
|
||||
public bool GetLinkedNode(out LinkedListNode<PlacementBase> node)
|
||||
{
|
||||
if (_linkNode == null)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
node = _linkNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetLinkedNode(LinkedListNode<PlacementBase> node)
|
||||
{
|
||||
_linkNode = node;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 重写 - ISerializablePost
|
||||
|
||||
public override SerializerHelper.Type.SerializeUnion SerializedOccurs()
|
||||
{
|
||||
return base.SerializedOccurs();
|
||||
}
|
||||
public override bool CheckDeserializeUnion(SerializerHelper.Type.SerializeUnion context)
|
||||
{
|
||||
return base.CheckDeserializeUnion(context);
|
||||
}
|
||||
public override void DeserializeOccurs(SerializerHelper.Type.SerializeUnion context)
|
||||
{
|
||||
base.DeserializeOccurs(context);
|
||||
}
|
||||
public override void DeserializeHysteresisOccurs(SerializerHelper.Type.SerializeUnion context)
|
||||
{
|
||||
base.DeserializeHysteresisOccurs(context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d376a769768d6b44d8759b7e7ab9f327
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Deconstruction.Element;
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.Debuger;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace SesothoLine
|
||||
{
|
||||
/// <summary>
|
||||
/// 点,承担连接的责任
|
||||
/// </summary>
|
||||
public class TerminalPoint : Deconstruction.Element.ContactPoint,
|
||||
ILinkconfidentPe // 链表跟踪
|
||||
{
|
||||
private LinkedListNode<PlacementBase> _linkNode;
|
||||
|
||||
#region 生命周期
|
||||
|
||||
// 调试绘制一下位置
|
||||
// private void Update()
|
||||
// {
|
||||
// MacroDebugDraw.DrawDownArrow(transform.position + MacroMath.RandomVector3(0.1f, Identifier), Color.green);
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实现 - ILinkconfidentPe
|
||||
|
||||
public bool GetLinkedNode(out LinkedListNode<PlacementBase> node)
|
||||
{
|
||||
if (_linkNode == null)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
node = _linkNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetLinkedNode(LinkedListNode<PlacementBase> node)
|
||||
{
|
||||
_linkNode = node;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 重写 - ISerializablePost
|
||||
|
||||
public override SerializerHelper.Type.SerializeUnion SerializedOccurs()
|
||||
{
|
||||
return base.SerializedOccurs();
|
||||
}
|
||||
public override bool CheckDeserializeUnion(SerializerHelper.Type.SerializeUnion context)
|
||||
{
|
||||
return base.CheckDeserializeUnion(context);
|
||||
}
|
||||
public override void DeserializeOccurs(SerializerHelper.Type.SerializeUnion context)
|
||||
{
|
||||
base.DeserializeOccurs(context);
|
||||
}
|
||||
public override void DeserializeHysteresisOccurs(SerializerHelper.Type.SerializeUnion context)
|
||||
{
|
||||
base.DeserializeHysteresisOccurs(context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afcf586b1fc68ab4bac3cd8438b48ea7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user