feat(ai): AiDefinitionRegistry——反射按id提供共享AiGraph(关域重载重置)
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using BaseGames.AI;
|
||||||
|
|
||||||
|
namespace BaseGames.Tests.EditMode.AI
|
||||||
|
{
|
||||||
|
// 顶层测试定义,供反射注册表发现
|
||||||
|
[AiDefinition("__test_grunt")]
|
||||||
|
internal sealed class TestGruntAi : AiScript
|
||||||
|
{
|
||||||
|
protected override void Build(BrainBuilder b)
|
||||||
|
{
|
||||||
|
b.Entry("A");
|
||||||
|
b.State("A").To("B").When(c => c.Sensor.SeesPlayer(), "SeesPlayer");
|
||||||
|
b.State("B");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AiDefinitionRegistryTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Has_FindsRegisteredDefinition()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(AiDefinitionRegistry.Has("__test_grunt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetGraph_ResolvesById()
|
||||||
|
{
|
||||||
|
var g = AiDefinitionRegistry.GetGraph("__test_grunt");
|
||||||
|
Assert.AreEqual("A", g.EntryState);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetGraph_SameId_ReturnsSharedInstance()
|
||||||
|
{
|
||||||
|
Assert.AreSame(
|
||||||
|
AiDefinitionRegistry.GetGraph("__test_grunt"),
|
||||||
|
AiDefinitionRegistry.GetGraph("__test_grunt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetGraph_UnknownId_Throws()
|
||||||
|
{
|
||||||
|
Assert.Throws<System.InvalidOperationException>(
|
||||||
|
() => AiDefinitionRegistry.GetGraph("__nope__"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 830591c077a45b54f9f56e6debdb035b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace BaseGames.AI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 反射收集所有 [AiDefinition] 的 AiScript 子类,按 id 提供共享 AiGraph(flyweight)。
|
||||||
|
/// 同一 id 全实例共享同一 AiScript 实例 → 同一张不可变 AiGraph。
|
||||||
|
/// </summary>
|
||||||
|
public static class AiDefinitionRegistry
|
||||||
|
{
|
||||||
|
static Dictionary<string, AiScript> _byId;
|
||||||
|
|
||||||
|
// 项目已关闭 Domain Reload:静态缓存需在进入播放时重置,保证按最新类型重建。
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
|
static void ResetOnPlay() => _byId = null;
|
||||||
|
|
||||||
|
static void EnsureBuilt()
|
||||||
|
{
|
||||||
|
if (_byId != null) return;
|
||||||
|
_byId = new Dictionary<string, AiScript>();
|
||||||
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
Type[] types;
|
||||||
|
try { types = asm.GetTypes(); }
|
||||||
|
catch (ReflectionTypeLoadException e) { types = e.Types; }
|
||||||
|
if (types == null) continue;
|
||||||
|
for (int i = 0; i < types.Length; i++)
|
||||||
|
{
|
||||||
|
var t = types[i];
|
||||||
|
if (t == null || t.IsAbstract || !typeof(AiScript).IsAssignableFrom(t)) continue;
|
||||||
|
var attr = t.GetCustomAttribute<AiDefinitionAttribute>();
|
||||||
|
if (attr == null) continue;
|
||||||
|
if (_byId.ContainsKey(attr.Id))
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"AiDefinitionRegistry: 重复的 AI 定义 id '{attr.Id}'({t.FullName})。");
|
||||||
|
_byId[attr.Id] = (AiScript)Activator.CreateInstance(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Has(string id)
|
||||||
|
{
|
||||||
|
EnsureBuilt();
|
||||||
|
return _byId.ContainsKey(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>取 id 对应的共享 AiGraph;不存在则显式抛错(根因暴露,不做兜底)。</summary>
|
||||||
|
public static AiGraph GetGraph(string id)
|
||||||
|
{
|
||||||
|
EnsureBuilt();
|
||||||
|
if (!_byId.TryGetValue(id, out var script))
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"AiDefinitionRegistry: 未找到 AI 定义 id '{id}'。请确认存在 [AiDefinition(\"{id}\")] 的 AiScript 子类。");
|
||||||
|
return script.GetOrBuildGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> Ids
|
||||||
|
{
|
||||||
|
get { EnsureBuilt(); return _byId.Keys; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3becea437ed3fcf45b936e3a26054adf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Reference in New Issue
Block a user