- AiRecipeSO 新增 protected InvalidateGraph(),子类装配模块后调用以丢弃缓存图,
避免改动要等到下次进 Play 才生效的静默陈旧图问题。
- PerceptionRecipeSO.SetModulesForTests 改名为 AssignModules(向导也要用它装配模块,
旧名字具有误导性),并在装配后调用 InvalidateGraph。
- 新增 EnemyAiRecipeWizard:按 AssetFolderSpec 定名定路径创建感知型 AI 配方资产
(Assets/_Game/Data/Enemies/{EnemyID}/ENM_{EnemyID}_Ai.asset),已存在则选中不覆盖。
- PerceptionRecipeSoTests 同步改名调用点,并新增测试钉住"装配后缓存必须失效"契约。
75 lines
2.6 KiB
C#
75 lines
2.6 KiB
C#
using System.IO;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using BaseGames.Enemies;
|
||
|
||
namespace BaseGames.Editor.AI
|
||
{
|
||
/// <summary>
|
||
/// 敌人 AI 配方脚手架。按 AssetFolderSpec 定名定路径:
|
||
/// Assets/_Game/Data/Enemies/{EnemyID}/ENM_{EnemyID}_Ai.asset
|
||
/// </summary>
|
||
public sealed class EnemyAiRecipeWizard : EditorWindow
|
||
{
|
||
const string DataRoot = "Assets/_Game/Data/Enemies";
|
||
|
||
string _enemyId = "E001";
|
||
|
||
[MenuItem("BaseGames/AI/Enemy AI Recipe Wizard")]
|
||
static void Open() => GetWindow<EnemyAiRecipeWizard>("敌人 AI 配方向导").minSize = new Vector2(420, 160);
|
||
|
||
void OnGUI()
|
||
{
|
||
EditorGUILayout.HelpBox(
|
||
"创建感知型 AI 配方资产。创建后在 Inspector 里选两层模块(未发现 / 交战)," +
|
||
"再把资产拖到敌人预制体的 EnemyAiBrain._recipe 上。",
|
||
MessageType.Info);
|
||
|
||
_enemyId = EditorGUILayout.TextField("敌人 ID(如 E001)", _enemyId);
|
||
|
||
string path = TargetPath(_enemyId);
|
||
EditorGUILayout.LabelField("产出路径", path);
|
||
|
||
using (new EditorGUI.DisabledScope(string.IsNullOrWhiteSpace(_enemyId)))
|
||
{
|
||
if (GUILayout.Button("创建配方资产", GUILayout.Height(28)))
|
||
Create(_enemyId);
|
||
}
|
||
}
|
||
|
||
static string TargetPath(string enemyId) =>
|
||
$"{DataRoot}/{enemyId}/ENM_{enemyId}_Ai.asset";
|
||
|
||
/// <summary>建配方资产。已存在则选中返回,不覆盖用户已配好的内容。</summary>
|
||
public static PerceptionRecipeSO Create(string enemyId)
|
||
{
|
||
string dir = $"{DataRoot}/{enemyId}";
|
||
if (!AssetDatabase.IsValidFolder(dir))
|
||
{
|
||
Directory.CreateDirectory(dir);
|
||
AssetDatabase.Refresh();
|
||
}
|
||
|
||
string path = TargetPath(enemyId);
|
||
var existing = AssetDatabase.LoadAssetAtPath<PerceptionRecipeSO>(path);
|
||
if (existing != null)
|
||
{
|
||
Selection.activeObject = existing;
|
||
EditorGUIUtility.PingObject(existing);
|
||
Debug.Log($"配方已存在,已选中:{path}");
|
||
return existing;
|
||
}
|
||
|
||
var so = ScriptableObject.CreateInstance<PerceptionRecipeSO>();
|
||
AssetDatabase.CreateAsset(so, path);
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
|
||
Selection.activeObject = so;
|
||
EditorGUIUtility.PingObject(so);
|
||
Debug.Log($"✔ 已创建 AI 配方:{path}");
|
||
return so;
|
||
}
|
||
}
|
||
}
|