架构大更
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Character
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用新版 SerializedDictionary 和 CharacterAttributePair 的全新配置集合类。
|
||||
/// (目前不替换旧的 CharacterAttributesDefaultCollection,请按需测试本版本)
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "CharacterAttributeCollection",
|
||||
menuName = "Continentis/MainGame/Character/CharacterAttributeCollection", order = 1)]
|
||||
public class CharacterAttributeCollection : ScriptableObject
|
||||
{
|
||||
[Title("Attributes(新特性架构测试版)")] [Tooltip("角色的通常属性:支持下拉菜单/手写自由切换的优雅 UI 交互!")]
|
||||
public SerializedDictionary<string, float, CharacterAttributePair> originalAttributes;
|
||||
|
||||
[Tooltip("初始化时赋予给CurrentAttributes的属性:\n" +
|
||||
"留空则默认为0,填数字则使用数字。")]
|
||||
public SerializedDictionary<string, string> runtimeAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义的带有极简美学 UI 的字典序列化结构体。
|
||||
/// 可以同时包含“下拉菜单安全输入”以及“纯手写高级输入”两种状态。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct CharacterAttributePair : ISerializedPair<string, float>
|
||||
{
|
||||
[SerializeField] [HideInInspector] public string attributeKey;
|
||||
[SerializeField] [HideInInspector] private bool useManualInput;
|
||||
|
||||
[ShowInInspector]
|
||||
[PropertyOrder(0)]
|
||||
[HideIf("useManualInput")]
|
||||
[HorizontalGroup("H")]
|
||||
[HideLabel]
|
||||
[ValueDropdown("@Continentis.MainGame.Base.EditorBaseCollection.GetCharacterAttributesDropdown($property)",
|
||||
IsUniqueList = true, DropdownHeight = 400)]
|
||||
[InlineButton("ToggleMode", Icon = SdfIconType.ListUl, Label = "")]
|
||||
public string DropdownKey
|
||||
{
|
||||
get => attributeKey;
|
||||
set => attributeKey = value;
|
||||
}
|
||||
|
||||
[ShowInInspector]
|
||||
[PropertyOrder(0)]
|
||||
[ShowIf("useManualInput")]
|
||||
[HorizontalGroup("H")]
|
||||
[HideLabel]
|
||||
[InlineButton("ToggleMode", Icon = SdfIconType.PencilSquare, Label = "")]
|
||||
public string ManualKey
|
||||
{
|
||||
get => attributeKey;
|
||||
set => attributeKey = value;
|
||||
}
|
||||
|
||||
[PropertyOrder(10)] [HorizontalGroup("H", MarginLeft = 10, Width = 100)] [HideLabel]
|
||||
public float attributeValue;
|
||||
|
||||
// 供 SLSUtilities.General 的接口实现
|
||||
public string Key
|
||||
{
|
||||
get => attributeKey;
|
||||
set => attributeKey = value;
|
||||
}
|
||||
|
||||
public float Value
|
||||
{
|
||||
get => attributeValue;
|
||||
set => attributeValue = value;
|
||||
}
|
||||
|
||||
private void ToggleMode()
|
||||
{
|
||||
useManualInput = !useManualInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9de2fe8a62ebef640ae5ce8697f0f5b0
|
||||
@@ -1,24 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using NaughtyAttributes;
|
||||
using SLSFramework.General;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Continentis.MainGame.Character
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Continentis/MainGame/Character/AttributesDefaultCollection", fileName = "CharacterAttributesDefaultCollection")]
|
||||
/// <summary>
|
||||
/// 角色属性默认值模板。
|
||||
/// 放置于 Assets/Mods/{ModName}/Characters/DefaultCollections/ 路径下才会被 PasteDefaultAttributes 识别并导入。
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Continentis/MainGame/Character/AttributesDefaultCollection",
|
||||
fileName = "CharacterAttributesDefaultCollection")]
|
||||
public class CharacterAttributesDefaultCollection : ScriptableObject
|
||||
{
|
||||
[Header("Attributes")]
|
||||
[KeyWidth(0.75f)]
|
||||
[TitleGroup("属性模板")]
|
||||
|
||||
[DictionaryDrawerSettings(KeyLabel = "属性名", ValueLabel = "属性值")]
|
||||
[LabelText("核心属性 (Core)")]
|
||||
[Tooltip("对应 CharacterData.coreAttributes")]
|
||||
public SerializableDictionary<string, float> coreAttributes;
|
||||
|
||||
[KeyWidth(0.75f)]
|
||||
|
||||
[DictionaryDrawerSettings(KeyLabel = "属性名", ValueLabel = "属性值")]
|
||||
[LabelText("通常属性 (General)")]
|
||||
[Tooltip("对应 CharacterData.generalAttributes")]
|
||||
public SerializableDictionary<string, float> generalAttributes;
|
||||
|
||||
|
||||
[FormerlySerializedAs("endowingGeneralAttributes")]
|
||||
[KeyWidth(0.5f)]
|
||||
[Tooltip("初始化时赋予给CurrentGeneralAttributes的属性:第一栏是属性名,第二栏是初始化时使用对应名称的GeneralAttributes的数据(或一个常数),留空则默认为0")]
|
||||
[DictionaryDrawerSettings(KeyLabel = "属性名", ValueLabel = "初始值表达式")]
|
||||
[LabelText("运行时通常属性 (Runtime General)")]
|
||||
[Tooltip("初始化时赋予给 CurrentGeneralAttributes 的属性;Value 填写对应 GeneralAttributes 的 Key 名(或一个常数),留空则默认为 0。")]
|
||||
public SerializableDictionary<string, string> runtimeGeneralAttributes;
|
||||
}
|
||||
}
|
||||
@@ -1,129 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Card;
|
||||
using Continentis.MainGame.UI;
|
||||
using NaughtyAttributes;
|
||||
using SLSFramework.General;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSFramework.UModAssistance;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Continentis.MainGame.Character
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// CharacterData — ScriptableObject 数据定义
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
[CreateAssetMenu(menuName = "Continentis/MainGame/Character/CharacterData", fileName = "CharacterData")]
|
||||
public partial class CharacterData : ScriptableObject
|
||||
{
|
||||
[Header("Fundamental")]
|
||||
// ── Fundamental ───────────────────────────────────────────────────────
|
||||
|
||||
[BoxGroup("Fundamental"), PropertyOrder(0)]
|
||||
[LabelText("使用自定义逻辑类")]
|
||||
[Tooltip("勾选后可通过下拉菜单选择一个 CharacterLogicBase 子类,并自动填充 modName / className。")]
|
||||
public bool haveCustomClass;
|
||||
|
||||
// haveCustomClass = true 时:逻辑类选择器(替代旧 DrawSearchableTypeSelector)
|
||||
[BoxGroup("Fundamental"), PropertyOrder(1)]
|
||||
[ShowIf("haveCustomClass")]
|
||||
[InfoBox("请通过下拉菜单选择角色逻辑类,Mod 名 / 类名将自动填充。", InfoMessageType.None)]
|
||||
[ValueDropdown("@CharacterData.GetCharacterLogicDropdownItems()", AppendNextDrawer = true, DisableGUIInAppendedDrawer = true)]
|
||||
[LabelText("逻辑类全名"), OnValueChanged("OnCharacterLogicClassSelected")]
|
||||
public string classFullName;
|
||||
|
||||
// haveCustomClass = false 时:手动填写 modName / className
|
||||
[BoxGroup("Fundamental"), PropertyOrder(2)]
|
||||
[HideIf("haveCustomClass")]
|
||||
[LabelText("Mod 名称")]
|
||||
public string modName;
|
||||
|
||||
[BoxGroup("Fundamental"), PropertyOrder(3)]
|
||||
[HideIf("haveCustomClass")]
|
||||
[LabelText("类名称")]
|
||||
public string className;
|
||||
|
||||
[BoxGroup("Fundamental"), PropertyOrder(4)]
|
||||
[LabelText("显示名称 Key")]
|
||||
public string displayName;
|
||||
|
||||
[BoxGroup("Fundamental"), PropertyOrder(5)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = true)]
|
||||
[LabelText("标签")]
|
||||
public List<string> tags;
|
||||
|
||||
|
||||
// ── 立绘与描述 ────────────────────────────────────────────────────────
|
||||
|
||||
[BoxGroup("Visuals"), PropertyOrder(6)]
|
||||
[PreviewField(80, ObjectFieldAlignment.Left), LabelText("头像 (Avatar)")]
|
||||
public Sprite avatar;
|
||||
|
||||
[BoxGroup("Visuals"), PropertyOrder(7)]
|
||||
[PreviewField(120, ObjectFieldAlignment.Left), LabelText("立绘 (Portrait)")]
|
||||
public Sprite portrait;
|
||||
|
||||
|
||||
[BoxGroup("Visuals"), PropertyOrder(8)]
|
||||
[LabelText("角色描述 Key")]
|
||||
public string characterDescription;
|
||||
|
||||
[BoxGroup("Visuals"), PropertyOrder(9)]
|
||||
[LabelText("角色故事 Key")]
|
||||
public string characterStory;
|
||||
|
||||
[Header("Attributes")]
|
||||
[Tooltip("角色的核心属性:第一栏是属性名,第二栏是属性值")]
|
||||
public SerializableDictionary<string, float> coreAttributes;
|
||||
|
||||
[Tooltip("角色的通常属性:第一栏是属性名,第二栏是属性值")]
|
||||
public SerializableDictionary<string, float> generalAttributes;
|
||||
|
||||
[Tooltip("初始化时赋予给CurrentGeneralAttributes的属性:第一栏是属性名,第二栏是初始化时使用对应名称的GeneralAttributes的数据,留空则默认为0,如果是float数字则直接使用该数字")]
|
||||
public SerializableDictionary<string, string> runtimeGeneralAttributes;
|
||||
|
||||
[Header("View")]
|
||||
|
||||
// ── Attributes ────────────────────────────────────────────────────────
|
||||
|
||||
[TabGroup("Data", "属性"), PropertyOrder(20)]
|
||||
[Tooltip("角色的核心属性:支持智能下拉菜单")]
|
||||
public SerializedDictionary<string, float, CharacterAttributePair> coreAttributes = new SerializedDictionary<string, float, CharacterAttributePair>();
|
||||
|
||||
[TabGroup("Data", "属性"), PropertyOrder(21)]
|
||||
[Tooltip("角色的通常属性:支持智能下拉菜单")]
|
||||
public SerializedDictionary<string, float, CharacterAttributePair> generalAttributes = new SerializedDictionary<string, float, CharacterAttributePair>();
|
||||
|
||||
[TabGroup("Data", "属性"), PropertyOrder(22)]
|
||||
[DictionaryDrawerSettings(KeyLabel = "运行时状态槽 (如 Health)", ValueLabel = "绑定的最大值标记 (如 MaximumHealth)")]
|
||||
[Tooltip("初始化时赋予给 CurrentGeneralAttributes 的属性;Value 填写对应 GeneralAttributes 的 Key 名,或直接填浮点数,留空默认为 0。")]
|
||||
public SerializedDictionary<string, string> runtimeGeneralAttributes = new SerializedDictionary<string, string>();
|
||||
|
||||
// ── View ──────────────────────────────────────────────────────────────
|
||||
|
||||
[TabGroup("Data", "视图"), PropertyOrder(30)]
|
||||
[LabelText("战斗位置顺序")]
|
||||
[MinValue(0)]
|
||||
public int combatPositionOrder;
|
||||
|
||||
[TabGroup("Data", "视图"), PropertyOrder(31)]
|
||||
[LabelText("战斗角色视图 Prefab")]
|
||||
public GameObject combatCharacterView;
|
||||
|
||||
[TabGroup("Data", "视图"), PropertyOrder(32)]
|
||||
[DictionaryDrawerSettings(KeyLabel = "动画名称", ValueLabel = "AnimationClip")]
|
||||
[LabelText("动画映射")]
|
||||
public SerializableDictionary<string, AnimationClip> animations;
|
||||
|
||||
[Header("References")]
|
||||
|
||||
// ── References ────────────────────────────────────────────────────────
|
||||
|
||||
[TabGroup("Data", "引用"), PropertyOrder(40)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
|
||||
[ValueDropdown("GetAvailablePrefabs", AppendNextDrawer = true)]
|
||||
[LabelText("Prefab 引用")]
|
||||
public List<string> prefabRefs = new List<string>();
|
||||
|
||||
[TabGroup("Data", "引用"), PropertyOrder(41)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
|
||||
[ValueDropdown("GetAvailableCardData", AppendNextDrawer = true)]
|
||||
[LabelText("衍生卡牌数据引用")]
|
||||
public List<string> derivativeCardDataRefs = new List<string>();
|
||||
|
||||
[TabGroup("Data", "引用"), PropertyOrder(42)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
|
||||
[ValueDropdown("GetAvailableCharacterData", AppendNextDrawer = true)]
|
||||
[LabelText("衍生角色数据引用")]
|
||||
public List<string> derivativeCharacterDataRefs = new List<string>();
|
||||
|
||||
[TabGroup("Data", "引用"), PropertyOrder(43)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
|
||||
[ValueDropdown("GetAvailableCardData", AppendNextDrawer = true)]
|
||||
[Tooltip("初始卡组引用列表")]
|
||||
[LabelText("初始卡组引用")]
|
||||
public List<string> initialDeckRef;
|
||||
[Tooltip("HUD信息引用列表")]
|
||||
|
||||
[TabGroup("Data", "引用"), PropertyOrder(44)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
|
||||
[ValueDropdown("GetAvailableHUDData", AppendNextDrawer = true)]
|
||||
[Tooltip("HUD 信息引用列表")]
|
||||
[LabelText("HUD 数据引用")]
|
||||
public List<string> hudDataRefs;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Runtime: 数据库查询
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
public partial class CharacterData
|
||||
{
|
||||
/// <summary>
|
||||
/// 通过 DataID 从 ModManager 数据库查找 CharacterData。
|
||||
/// </summary>
|
||||
public static CharacterData Get(string dataID)
|
||||
{
|
||||
return ModManager.GetData<CharacterData>(dataID);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public partial class CharacterData
|
||||
{
|
||||
public void PasteDefaultAttributes()
|
||||
{
|
||||
List<CharacterAttributesDefaultCollection> targetCollections = new List<CharacterAttributesDefaultCollection>();
|
||||
string[] guids = AssetDatabase.FindAssets("t:CharacterAttributesDefaultCollection");
|
||||
|
||||
if (guids.Length > 1)
|
||||
{
|
||||
Debug.Log("找到了多个CharacterAttributesDefaultCollection,它们将被合并后导入CharacterData");
|
||||
}
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
// 将GUID转换为资产的路径
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
|
||||
// 2. 验证每个资产的路径是否完全符合您指定的结构
|
||||
// 使用 Path.GetDirectoryName 获取文件所在的目录
|
||||
// 并统一使用'/'作为路径分隔符,以兼容不同操作系统
|
||||
string directory = Path.GetDirectoryName(path)?.Replace('\\', '/');
|
||||
Debug.Log($"Checking asset at path: {path}, directory: {directory}");
|
||||
|
||||
// 加载资产以检查其命名空间
|
||||
ScriptableObject collection = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
||||
Type assetType = collection.GetType();
|
||||
string assetNamespace = assetType.Namespace;
|
||||
|
||||
// 检查目录是否有效,是否在"Assets/Mods/"下,并且是否以"/Characters/DefaultCollections"结尾
|
||||
if (!string.IsNullOrEmpty(directory) &&
|
||||
assetNamespace == typeof(CharacterAttributesDefaultCollection).Namespace &&
|
||||
directory.StartsWith("Assets/Mods/") &&
|
||||
directory.EndsWith("/Characters/DefaultCollections"))
|
||||
{
|
||||
// 3. 如果路径和命名空间都符合要求,则将该资产添加到目标列表中
|
||||
CharacterAttributesDefaultCollection defaultList = collection as CharacterAttributesDefaultCollection;
|
||||
targetCollections.Add(defaultList);
|
||||
Debug.Log($"Loaded DefaultStringList from: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, float> coreAttributes = new Dictionary<string, float>();
|
||||
Dictionary<string, float> generalAttributes = new Dictionary<string, float>();
|
||||
Dictionary<string, string> runtimeGeneralAttributes = new Dictionary<string, string>();
|
||||
|
||||
foreach (CharacterAttributesDefaultCollection collection in targetCollections)
|
||||
{
|
||||
collection.coreAttributes.PasteDictionary(coreAttributes);
|
||||
collection.generalAttributes.PasteDictionary(generalAttributes);
|
||||
collection.runtimeGeneralAttributes.PasteDictionary(runtimeGeneralAttributes);
|
||||
}
|
||||
|
||||
coreAttributes.PasteDictionary(this.coreAttributes);
|
||||
generalAttributes.PasteDictionary(this.generalAttributes);
|
||||
runtimeGeneralAttributes.PasteDictionary(this.runtimeGeneralAttributes);
|
||||
Debug.Log($"Pasted default attributes to file {this.name}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user