feat(wizard): 敌人能力 SO 按能力类型创建正确子类(向导批量创建产出子类 SO)
This commit is contained in:
@@ -277,12 +277,13 @@ namespace BaseGames.Editor
|
|||||||
var factory = MakeActionGroup();
|
var factory = MakeActionGroup();
|
||||||
factory.Add(MakeFactoryButton($"ENM_{id}_Stats.asset", () => { CreateEnemyStatsSO(id); RefreshSOStatus(); }));
|
factory.Add(MakeFactoryButton($"ENM_{id}_Stats.asset", () => { CreateEnemyStatsSO(id); RefreshSOStatus(); }));
|
||||||
factory.Add(MakeFactoryButton($"ENM_{id}_AnimConfig.asset", () => { CreateEnemyAnimConfigSO(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 capturedName = ablName;
|
||||||
string capturedId = ablId;
|
string capturedId = ablId;
|
||||||
|
System.Type capturedType = soType;
|
||||||
factory.Add(MakeFactoryButton($"ABL_{id}_{capturedName}.asset",
|
factory.Add(MakeFactoryButton($"ABL_{id}_{capturedName}.asset",
|
||||||
() => { CreateEnemyAbilitySO(id, capturedName, capturedId); RefreshSOStatus(); }));
|
() => { CreateEnemyAbilitySO(id, capturedName, capturedId, capturedType); RefreshSOStatus(); }));
|
||||||
}
|
}
|
||||||
container.Add(factory);
|
container.Add(factory);
|
||||||
|
|
||||||
@@ -579,17 +580,23 @@ namespace BaseGames.Editor
|
|||||||
|
|
||||||
// ── SO 资产工厂:小怪(按类型) ───────────────────────────────────────────
|
// ── SO 资产工厂:小怪(按类型) ───────────────────────────────────────────
|
||||||
|
|
||||||
/// <summary>返回指定敌人类型的 (abilityFileName, abilityId) 定义列表。</summary>
|
/// <summary>返回指定敌人类型的 (abilityFileName, abilityId, soType) 定义列表。</summary>
|
||||||
private static (string ablName, string ablId)[] GetEnemyAbilityDefs(string enemyId) => enemyId switch
|
private static (string ablName, string ablId, System.Type soType)[] GetEnemyAbilityDefs(string enemyId) => enemyId switch
|
||||||
{
|
{
|
||||||
"E001" => new[] { ("Alert", "e001_alert"), ("Chase", "e001_chase") },
|
"E001" => new[] { ("Alert", "e001_alert", typeof(EnemyAbilitySO)),
|
||||||
"E002" => new[] { ("CeilingStrike", "e002_ceiling_strike") },
|
("Chase", "e001_chase", typeof(ContactChaseAbilitySO)) },
|
||||||
"E003" => new[] { ("Fall", "e003_fall") },
|
"E002" => new[] { ("CeilingStrike", "e002_ceiling_strike", typeof(CeilingHangStrikeAbilitySO)) },
|
||||||
"E004" => new[] { ("Appear", "e004_appear"), ("Bite", "e004_bite"), ("HeadSlam", "e004_headslam"),
|
"E003" => new[] { ("Fall", "e003_fall", typeof(AnimatedCeilingDropAbilitySO)) },
|
||||||
("Acid", "e004_acid"), ("Flip", "e004_flip") },
|
"E004" => new[] { ("Appear", "e004_appear", typeof(AppearAbilitySO)),
|
||||||
"E005" => new[] { ("Bite", "e005_bite"), ("Acid", "e005_acid") },
|
("Bite", "e004_bite", typeof(EnemyAbilitySO)),
|
||||||
"E006" => new[] { ("Leap", "e006_leap"), ("Chase", "e006_chase") },
|
("HeadSlam", "e004_headslam", typeof(RepeatSlamAbilitySO)),
|
||||||
_ => System.Array.Empty<(string, string)>(),
|
("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)
|
private static void CreateEnemyStatsSO(string id)
|
||||||
@@ -604,11 +611,11 @@ namespace BaseGames.Editor
|
|||||||
EditorScaffoldUtils.CreateSOAsset<EnemyAnimationConfigSO>(dir, $"ENM_{id}_AnimConfig");
|
EditorScaffoldUtils.CreateSOAsset<EnemyAnimationConfigSO>(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 dir = $"Assets/_Game/Data/Enemies/{enemyId}/Abilities";
|
||||||
string name = $"ABL_{enemyId}_{ablName}";
|
string name = $"ABL_{enemyId}_{ablName}";
|
||||||
var so = EditorScaffoldUtils.CreateSOAsset<EnemyAbilitySO>(dir, name);
|
var so = EditorScaffoldUtils.CreateSOAsset(soType, dir, name) as EnemyAbilitySO;
|
||||||
// Set abilityId on newly-created SO (skip if already existed = null returned)
|
// Set abilityId on newly-created SO (skip if already existed = null returned)
|
||||||
if (so != null)
|
if (so != null)
|
||||||
{
|
{
|
||||||
@@ -622,8 +629,8 @@ namespace BaseGames.Editor
|
|||||||
{
|
{
|
||||||
CreateEnemyStatsSO(id);
|
CreateEnemyStatsSO(id);
|
||||||
CreateEnemyAnimConfigSO(id);
|
CreateEnemyAnimConfigSO(id);
|
||||||
foreach (var (ablName, ablId) in GetEnemyAbilityDefs(id))
|
foreach (var (ablName, ablId, soType) in GetEnemyAbilityDefs(id))
|
||||||
CreateEnemyAbilitySO(id, ablName, ablId);
|
CreateEnemyAbilitySO(id, ablName, ablId, soType);
|
||||||
AssetDatabase.SaveAssets();
|
AssetDatabase.SaveAssets();
|
||||||
EditorUtility.DisplayDialog("创建完成",
|
EditorUtility.DisplayDialog("创建完成",
|
||||||
$"全部 {id} SO 已创建(已存在的跳过)。\n请放置到场景后检查组件绑定。", "确定");
|
$"全部 {id} SO 已创建(已存在的跳过)。\n请放置到场景后检查组件绑定。", "确定");
|
||||||
@@ -769,7 +776,7 @@ namespace BaseGames.Editor
|
|||||||
($"ENM_{id}_Stats", FindAtPath<EnemyStatsSO>($"{dir}/ENM_{id}_Stats.asset")),
|
($"ENM_{id}_Stats", FindAtPath<EnemyStatsSO>($"{dir}/ENM_{id}_Stats.asset")),
|
||||||
($"ENM_{id}_AnimConfig",FindAtPath<EnemyAnimationConfigSO>($"{dir}/ENM_{id}_AnimConfig.asset")),
|
($"ENM_{id}_AnimConfig",FindAtPath<EnemyAnimationConfigSO>($"{dir}/ENM_{id}_AnimConfig.asset")),
|
||||||
};
|
};
|
||||||
foreach (var (ablName, _) in GetEnemyAbilityDefs(id))
|
foreach (var (ablName, _, _) in GetEnemyAbilityDefs(id))
|
||||||
items.Add(($"ABL_{id}_{ablName}", FindAtPath<EnemyAbilitySO>($"{ablDir}/ABL_{id}_{ablName}.asset")));
|
items.Add(($"ABL_{id}_{ablName}", FindAtPath<EnemyAbilitySO>($"{ablDir}/ABL_{id}_{ablName}.asset")));
|
||||||
|
|
||||||
_enemyStatusPanel.Add(MakeStatusGrid(items.ToArray()));
|
_enemyStatusPanel.Add(MakeStatusGrid(items.ToArray()));
|
||||||
|
|||||||
@@ -171,6 +171,30 @@ namespace BaseGames.Editor
|
|||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按运行时 Type 创建 SO 资产(用于需要动态子类型的场景,如能力子类 SO)。
|
||||||
|
/// 若资产已存在则返回 null(不覆盖);成功后自动 Ping 并选中。
|
||||||
|
/// </summary>
|
||||||
|
public static ScriptableObject CreateSOAsset(System.Type soType, string folder, string assetName)
|
||||||
|
{
|
||||||
|
string dir = folder.TrimEnd('/');
|
||||||
|
string path = $"{dir}/{assetName}.asset";
|
||||||
|
|
||||||
|
if (AssetDatabase.LoadAssetAtPath<ScriptableObject>(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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过 SaveFilePanel 让用户选择名称和路径,交互式创建 SO 资产。
|
/// 通过 SaveFilePanel 让用户选择名称和路径,交互式创建 SO 资产。
|
||||||
/// 取消时返回 null。
|
/// 取消时返回 null。
|
||||||
|
|||||||
Reference in New Issue
Block a user