From 8bd24df11e8d45970e27a3a8ca54bc1b4aad5694 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Tue, 21 Jul 2026 11:40:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(wizard):=20=E6=95=8C=E4=BA=BA=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=20SO=20=E6=8C=89=E8=83=BD=E5=8A=9B=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=AD=A3=E7=A1=AE=E5=AD=90=E7=B1=BB(?= =?UTF-8?q?=E5=90=91=E5=AF=BC=E6=89=B9=E9=87=8F=E5=88=9B=E5=BB=BA=E4=BA=A7?= =?UTF-8?q?=E5=87=BA=E5=AD=90=E7=B1=BB=20SO)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Editor/Character/CharacterWizardWindow.cs | 41 +++++++++++-------- .../Editor/Shared/EditorScaffoldUtils.cs | 24 +++++++++++ 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/Assets/_Game/Scripts/Editor/Character/CharacterWizardWindow.cs b/Assets/_Game/Scripts/Editor/Character/CharacterWizardWindow.cs index 345576d3..d29e6cca 100644 --- a/Assets/_Game/Scripts/Editor/Character/CharacterWizardWindow.cs +++ b/Assets/_Game/Scripts/Editor/Character/CharacterWizardWindow.cs @@ -277,12 +277,13 @@ namespace BaseGames.Editor var factory = MakeActionGroup(); factory.Add(MakeFactoryButton($"ENM_{id}_Stats.asset", () => { CreateEnemyStatsSO(id); RefreshSOStatus(); })); factory.Add(MakeFactoryButton($"ENM_{id}_AnimConfig.asset", () => { CreateEnemyAnimConfigSO(id); RefreshSOStatus(); })); - foreach (var (ablName, ablId) in GetEnemyAbilityDefs(id)) + foreach (var (ablName, ablId, soType) in GetEnemyAbilityDefs(id)) { string capturedName = ablName; string capturedId = ablId; + System.Type capturedType = soType; factory.Add(MakeFactoryButton($"ABL_{id}_{capturedName}.asset", - () => { CreateEnemyAbilitySO(id, capturedName, capturedId); RefreshSOStatus(); })); + () => { CreateEnemyAbilitySO(id, capturedName, capturedId, capturedType); RefreshSOStatus(); })); } container.Add(factory); @@ -579,17 +580,23 @@ namespace BaseGames.Editor // ── SO 资产工厂:小怪(按类型) ─────────────────────────────────────────── - /// 返回指定敌人类型的 (abilityFileName, abilityId) 定义列表。 - private static (string ablName, string ablId)[] GetEnemyAbilityDefs(string enemyId) => enemyId switch + /// 返回指定敌人类型的 (abilityFileName, abilityId, soType) 定义列表。 + private static (string ablName, string ablId, System.Type soType)[] GetEnemyAbilityDefs(string enemyId) => enemyId switch { - "E001" => new[] { ("Alert", "e001_alert"), ("Chase", "e001_chase") }, - "E002" => new[] { ("CeilingStrike", "e002_ceiling_strike") }, - "E003" => new[] { ("Fall", "e003_fall") }, - "E004" => new[] { ("Appear", "e004_appear"), ("Bite", "e004_bite"), ("HeadSlam", "e004_headslam"), - ("Acid", "e004_acid"), ("Flip", "e004_flip") }, - "E005" => new[] { ("Bite", "e005_bite"), ("Acid", "e005_acid") }, - "E006" => new[] { ("Leap", "e006_leap"), ("Chase", "e006_chase") }, - _ => System.Array.Empty<(string, string)>(), + "E001" => new[] { ("Alert", "e001_alert", typeof(EnemyAbilitySO)), + ("Chase", "e001_chase", typeof(ContactChaseAbilitySO)) }, + "E002" => new[] { ("CeilingStrike", "e002_ceiling_strike", typeof(CeilingHangStrikeAbilitySO)) }, + "E003" => new[] { ("Fall", "e003_fall", typeof(AnimatedCeilingDropAbilitySO)) }, + "E004" => new[] { ("Appear", "e004_appear", typeof(AppearAbilitySO)), + ("Bite", "e004_bite", typeof(EnemyAbilitySO)), + ("HeadSlam", "e004_headslam", typeof(RepeatSlamAbilitySO)), + ("Acid", "e004_acid", typeof(EnemyAbilitySO)), + ("Flip", "e004_flip", typeof(FacePlayerAbilitySO)) }, + "E005" => new[] { ("Bite", "e005_bite", typeof(EnemyAbilitySO)), + ("Acid", "e005_acid", typeof(EnemyAbilitySO)) }, + "E006" => new[] { ("Leap", "e006_leap", typeof(EnemyAbilitySO)), + ("Chase", "e006_chase", typeof(ContactChaseAbilitySO)) }, + _ => System.Array.Empty<(string, string, System.Type)>(), }; private static void CreateEnemyStatsSO(string id) @@ -604,11 +611,11 @@ namespace BaseGames.Editor EditorScaffoldUtils.CreateSOAsset(dir, $"ENM_{id}_AnimConfig"); } - private static void CreateEnemyAbilitySO(string enemyId, string ablName, string ablId) + private static void CreateEnemyAbilitySO(string enemyId, string ablName, string ablId, System.Type soType) { string dir = $"Assets/_Game/Data/Enemies/{enemyId}/Abilities"; string name = $"ABL_{enemyId}_{ablName}"; - var so = EditorScaffoldUtils.CreateSOAsset(dir, name); + var so = EditorScaffoldUtils.CreateSOAsset(soType, dir, name) as EnemyAbilitySO; // Set abilityId on newly-created SO (skip if already existed = null returned) if (so != null) { @@ -622,8 +629,8 @@ namespace BaseGames.Editor { CreateEnemyStatsSO(id); CreateEnemyAnimConfigSO(id); - foreach (var (ablName, ablId) in GetEnemyAbilityDefs(id)) - CreateEnemyAbilitySO(id, ablName, ablId); + foreach (var (ablName, ablId, soType) in GetEnemyAbilityDefs(id)) + CreateEnemyAbilitySO(id, ablName, ablId, soType); AssetDatabase.SaveAssets(); EditorUtility.DisplayDialog("创建完成", $"全部 {id} SO 已创建(已存在的跳过)。\n请放置到场景后检查组件绑定。", "确定"); @@ -769,7 +776,7 @@ namespace BaseGames.Editor ($"ENM_{id}_Stats", FindAtPath($"{dir}/ENM_{id}_Stats.asset")), ($"ENM_{id}_AnimConfig",FindAtPath($"{dir}/ENM_{id}_AnimConfig.asset")), }; - foreach (var (ablName, _) in GetEnemyAbilityDefs(id)) + foreach (var (ablName, _, _) in GetEnemyAbilityDefs(id)) items.Add(($"ABL_{id}_{ablName}", FindAtPath($"{ablDir}/ABL_{id}_{ablName}.asset"))); _enemyStatusPanel.Add(MakeStatusGrid(items.ToArray())); diff --git a/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs b/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs index a443d5a5..7c627512 100644 --- a/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs +++ b/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs @@ -171,6 +171,30 @@ namespace BaseGames.Editor return asset; } + /// + /// 按运行时 Type 创建 SO 资产(用于需要动态子类型的场景,如能力子类 SO)。 + /// 若资产已存在则返回 null(不覆盖);成功后自动 Ping 并选中。 + /// + public static ScriptableObject CreateSOAsset(System.Type soType, string folder, string assetName) + { + string dir = folder.TrimEnd('/'); + string path = $"{dir}/{assetName}.asset"; + + if (AssetDatabase.LoadAssetAtPath(path) != null) + { + Debug.LogWarning($"[EditorScaffoldUtils] 资产已存在,跳过创建:{path}"); + return null; + } + + EnsureFolder(dir); + + var asset = ScriptableObject.CreateInstance(soType); + AssetDatabase.CreateAsset(asset, path); + AssetDatabase.SaveAssets(); + PingAndSelect(asset); + return asset; + } + /// /// 通过 SaveFilePanel 让用户选择名称和路径,交互式创建 SO 资产。 /// 取消时返回 null。