commit a2808b627d1d7d19398acae47bb1f39210c243da
Author: 陈思海 <1464576565@qq.com>
Date: Wed Apr 15 19:47:09 2026 +0800
1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..da556c8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,83 @@
+# UnityProject
+
+/[Ll]ibrary/
+/[Tt]emp/
+/[Oo]bj/
+/[Bb]uild/
+/[Bb]uilds/
+/[Ll]ogs/
+/[Mm]emoryCaptures/
+/EditorBuild/
+/[Aa]ssets/StreamingAssets
+/[Aa]ssets/StreamingAssets.meta
+/BuildBundleInfo/
+
+# Asset meta data should only be ignored when the corresponding asset is also ignored
+!/[Aa]ssets/**/*.meta
+
+# Uncomment this line if you wish to ignore the asset store tools plugin
+# /[Aa]ssets/AssetStoreTools*
+
+# Autogenerated Jetbrains Rider plugin
+[Aa]ssets/Plugins/Editor/JetBrains*
+
+# Visual Studio cache directory
+.vs/
+
+# Gradle cache directory
+.gradle/
+
+# Autogenerated VS/MD/Consulo solution and project files
+ExportedObj/
+.consulo/
+*.csproj
+*.unityproj
+*.sln
+*.suo
+*.tmp
+*.user
+*.userprefs
+*.pidb
+*.booproj
+*.svd
+*.pdb
+*.mdb
+*.opendb
+*.VC.db
+
+# Unity3D generated meta files
+*.pidb.meta
+*.pdb.meta
+*.mdb.meta
+
+# Unity3D generated file on crash reports
+sysinfo.txt
+
+# Builds
+*.apk
+
+# Crashlytics generated file
+crashlytics-build.properties
+
+#HybirdCLR(HuaTuo)
+/HybirdCLRData/
+[Hh]ybridCLRData/
+
+#AATemp
+[Aa]ssets/AATemp/
+[Aa]ssets/AATemp.meta
+
+# Custom AATest
+[Aa]ssets/AATest/
+[Aa]ssets/AATest.meta
+
+
+
+#Sandbox
+Sandbox/
+
+#MAC
+.DS_Store
+
+#Rider
+.idea/
diff --git a/Assets/.claude/settings.local.json b/Assets/.claude/settings.local.json
new file mode 100644
index 0000000..bc469a0
--- /dev/null
+++ b/Assets/.claude/settings.local.json
@@ -0,0 +1,22 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(where pdftotext:*)",
+ "Bash(pdftotext \"G:\\\\\\\\UnityProject\\\\\\\\CapabilitySystem\\\\\\\\Assets\\\\\\\\技术文档.pdf\" \"G:\\\\\\\\UnityProject\\\\\\\\CapabilitySystem\\\\\\\\Assets\\\\\\\\技术文档.txt\")",
+ "Bash(ls -1 CapabilitySystem/Core/*.cs)",
+ "Bash(pdftotext \"技术文档.pdf\" - | head -n 500)",
+ "Bash(pdftotext \"技术文档.pdf\" - | tail -n +500 | head -n 500)",
+ "Bash(pdftotext \"技术文档.pdf\" /tmp/tech_doc.txt)",
+ "Read(//tmp/**)",
+ "Bash(pdftotext \"技术文档.pdf\" - 2>/dev/null)",
+ "Bash(grep '\"role\":\"user\"' \"C:\\\\Users\\\\admin\\\\.claude\\\\projects\\\\G--UnityProject-CapabilitySystem-Assets\\\\8203d8cd-1a37-42bd-8ba3-21ec01f49b28.jsonl\" | tail -1 | python -c \"import sys, json; data = json.load\\(sys.stdin\\); print\\(data['message']['content'][0]['text']\\)\")",
+ "Bash(grep '\"role\":\"user\"' \"C:\\\\Users\\\\admin\\\\.claude\\\\projects\\\\G--UnityProject-CapabilitySystem-Assets\\\\8203d8cd-1a37-42bd-8ba3-21ec01f49b28.jsonl\" | tail -1 | python -c \"import sys, json; data = json.load\\(sys.stdin\\); content = data['message']['content']; print\\(content if isinstance\\(content, str\\) else content[0] if isinstance\\(content, list\\) else content\\)\")",
+ "Bash(python3 -c \"import PyPDF2; pdf = PyPDF2.PdfReader\\('/g/UnityProject/CapabilitySystem/Assets/技术文档.pdf'\\); print\\(f'页数: {len\\(pdf.pages\\)}'\\); [print\\(f'--- 第{i+1}页 ---\\\\n{pdf.pages[i].extract_text\\(\\)}'\\) for i in range\\(min\\(5, len\\(pdf.pages\\)\\)\\)]\" 2>/dev/null || python3 -c \"import pdfplumber; pdf = pdfplumber.open\\('/g/UnityProject/CapabilitySystem/Assets/技术文档.pdf'\\); print\\(f'页数: {len\\(pdf.pages\\)}'\\); [print\\(f'--- 第{i+1}页 ---\\\\n{pdf.pages[i].extract_text\\(\\)}'\\) for i in range\\(min\\(5, len\\(pdf.pages\\)\\)\\)]\" 2>/dev/null || echo \"需要安装PDF解析库\")",
+ "Bash(pip install:*)",
+ "Bash(python3 -c \"\nimport pdfplumber\npdf = pdfplumber.open\\('/g/UnityProject/CapabilitySystem/Assets/技术文档.pdf'\\)\ntotal = len\\(pdf.pages\\)\nprint\\(f'总页数: {total}'\\)\nfor i in range\\(min\\(10, total\\)\\):\n text = pdf.pages[i].extract_text\\(\\)\n if text:\n print\\(f'\\\\n===== 第{i+1}页 ====='\\)\n print\\(text[:2000]\\)\n\" 2>&1)",
+ "Bash(python --version 2>&1; where python 2>&1; ls /g/UnityProject/CapabilitySystem/Assets/技术文档.pdf)",
+ "Bash(python -c \"import pdfplumber; print\\('pdfplumber ok'\\)\" 2>&1)",
+ "Bash(python -c \"\nimport pdfplumber\npdf = pdfplumber.open\\('G:/UnityProject/CapabilitySystem/Assets/技术文档.pdf'\\)\ntotal = len\\(pdf.pages\\)\nprint\\(f'总页数: {total}'\\)\nfor i in range\\(min\\(8, total\\)\\):\n text = pdf.pages[i].extract_text\\(\\)\n if text:\n print\\(f'\\\\n===== 第{i+1}页 ====='\\)\n print\\(text[:3000]\\)\npdf.close\\(\\)\n\" 2>&1)"
+ ]
+ }
+}
diff --git a/Assets/CapabilitySystem.meta b/Assets/CapabilitySystem.meta
new file mode 100644
index 0000000..81fb0b2
--- /dev/null
+++ b/Assets/CapabilitySystem.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bbdaf55c33b53e34b81f7e2588d66cf7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/CapabilitySystem/Attributes.meta b/Assets/CapabilitySystem/Attributes.meta
new file mode 100644
index 0000000..14b1190
--- /dev/null
+++ b/Assets/CapabilitySystem/Attributes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5ea2f924fbe109c44be5ebb61f540d30
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/CapabilitySystem/Attributes/TickOrderAttribute.cs b/Assets/CapabilitySystem/Attributes/TickOrderAttribute.cs
new file mode 100644
index 0000000..034f7ef
--- /dev/null
+++ b/Assets/CapabilitySystem/Attributes/TickOrderAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace CapabilitySystem
+{
+ ///
+ /// 标记Capability的Tick执行顺序
+ ///
+ [AttributeUsage(AttributeTargets.Class, Inherited = true)]
+ public class TickOrderAttribute : Attribute
+ {
+ public TickGroup TickGroup { get; }
+ public int Order { get; }
+
+ ///
+ /// 定义Capability的执行顺序
+ ///
+ /// Tick组
+ /// 组内顺序(默认0,数值越小越先执行)
+ public TickOrderAttribute(TickGroup tickGroup, int order = 0)
+ {
+ TickGroup = tickGroup;
+ Order = order;
+ }
+ }
+}
diff --git a/Assets/CapabilitySystem/Attributes/TickOrderAttribute.cs.meta b/Assets/CapabilitySystem/Attributes/TickOrderAttribute.cs.meta
new file mode 100644
index 0000000..86ca2eb
--- /dev/null
+++ b/Assets/CapabilitySystem/Attributes/TickOrderAttribute.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 08c8672f9dd000447aef58db8940efd1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/CapabilitySystem/Core.meta b/Assets/CapabilitySystem/Core.meta
new file mode 100644
index 0000000..5cb8606
--- /dev/null
+++ b/Assets/CapabilitySystem/Core.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e49e089b37c87894696d6f3b49563abf
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/CapabilitySystem/Core/ActionCapability.cs b/Assets/CapabilitySystem/Core/ActionCapability.cs
new file mode 100644
index 0000000..2f81212
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/ActionCapability.cs
@@ -0,0 +1,73 @@
+using UnityEngine;
+
+namespace CapabilitySystem
+{
+ ///
+ /// Action Capability基类
+ /// 配合ActionQueue使用,用于队列化的行为
+ ///
+ public abstract class ActionCapability : Capability
+ {
+ protected ActionQueue actionQueue;
+ protected bool isActionComplete;
+
+ protected override void Setup()
+ {
+ base.Setup();
+ actionQueue = new ActionQueue();
+ }
+
+ protected override bool ShouldActivate()
+ {
+ // 由外部控制激活(通过ActionQueue)
+ return false;
+ }
+
+ protected override void OnActivated()
+ {
+ base.OnActivated();
+ isActionComplete = false;
+ SetupActions();
+ actionQueue.Start();
+ }
+
+ protected override void TickActive(float deltaTime)
+ {
+ base.TickActive(deltaTime);
+ actionQueue.Update(deltaTime);
+
+ // 检查队列是否完成
+ if (!actionQueue.IsRunning)
+ {
+ isActionComplete = true;
+ }
+ }
+
+ protected override bool ShouldDeactivate()
+ {
+ return isActionComplete;
+ }
+
+ protected override void OnDeactivated()
+ {
+ actionQueue.Clear();
+ base.OnDeactivated();
+ }
+
+ ///
+ /// 子类实现:设置Action队列
+ ///
+ protected abstract void SetupActions();
+
+ ///
+ /// 手动激活此ActionCapability
+ ///
+ public void Execute()
+ {
+ if (!IsActive)
+ {
+ Activate();
+ }
+ }
+ }
+}
diff --git a/Assets/CapabilitySystem/Core/ActionCapability.cs.meta b/Assets/CapabilitySystem/Core/ActionCapability.cs.meta
new file mode 100644
index 0000000..4b4d012
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/ActionCapability.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 2c5f43ff96d913240bd0d7d270a05814
\ No newline at end of file
diff --git a/Assets/CapabilitySystem/Core/ActionQueue.cs b/Assets/CapabilitySystem/Core/ActionQueue.cs
new file mode 100644
index 0000000..44f0e73
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/ActionQueue.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace CapabilitySystem
+{
+ ///
+ /// Action队列系统
+ /// 用于管理顺序执行的Action,适用于Boss战斗和Puzzle系统
+ ///
+ public class ActionQueue
+ {
+ /// Action定义
+ public class ActionDefinition
+ {
+ public string Name;
+ public Action OnStart;
+ public Action OnUpdate;
+ public Func IsComplete;
+ public Action OnEnd;
+ public float Duration;
+ public bool UsesDuration;
+
+ public ActionDefinition(string name)
+ {
+ Name = name;
+ Duration = 0f;
+ UsesDuration = false;
+ }
+ }
+
+ private Queue actionQueue = new Queue();
+ private ActionDefinition currentAction;
+ private float currentActionTime;
+ private bool isRunning;
+
+ #region Properties
+
+ public bool IsRunning => isRunning;
+ public int QueueCount => actionQueue.Count;
+ public ActionDefinition CurrentAction => currentAction;
+
+ #endregion
+
+ #region Queue Management
+
+ ///
+ /// 添加Action到队列
+ ///
+ public void Enqueue(ActionDefinition action)
+ {
+ if (action == null)
+ {
+ Debug.LogWarning("[ActionQueue] Cannot enqueue null action");
+ return;
+ }
+
+ actionQueue.Enqueue(action);
+ }
+
+ ///
+ /// 添加基于时长的Action
+ ///
+ public void EnqueueDuration(string name, float duration, Action onStart = null, Action onUpdate = null, Action onEnd = null)
+ {
+ var action = new ActionDefinition(name)
+ {
+ Duration = duration,
+ UsesDuration = true,
+ OnStart = onStart,
+ OnUpdate = onUpdate,
+ OnEnd = onEnd
+ };
+ Enqueue(action);
+ }
+
+ ///
+ /// 添加基于条件的Action
+ ///
+ public void EnqueueCondition(string name, Func isComplete, Action onStart = null, Action onUpdate = null, Action onEnd = null)
+ {
+ var action = new ActionDefinition(name)
+ {
+ IsComplete = isComplete,
+ UsesDuration = false,
+ OnStart = onStart,
+ OnUpdate = onUpdate,
+ OnEnd = onEnd
+ };
+ Enqueue(action);
+ }
+
+ ///
+ /// 清空队列
+ ///
+ public void Clear()
+ {
+ actionQueue.Clear();
+ if (currentAction != null)
+ {
+ currentAction.OnEnd?.Invoke();
+ currentAction = null;
+ }
+ currentActionTime = 0f;
+ isRunning = false;
+ }
+
+ #endregion
+
+ #region Execution
+
+ ///
+ /// 开始执行队列
+ ///
+ public void Start()
+ {
+ if (isRunning)
+ {
+ Debug.LogWarning("[ActionQueue] Already running");
+ return;
+ }
+
+ isRunning = true;
+ StartNextAction();
+ }
+
+ ///
+ /// 停止执行
+ ///
+ public void Stop()
+ {
+ if (currentAction != null)
+ {
+ currentAction.OnEnd?.Invoke();
+ currentAction = null;
+ }
+ isRunning = false;
+ currentActionTime = 0f;
+ }
+
+ ///
+ /// 更新队列(需要在外部每帧调用)
+ ///
+ public void Update(float deltaTime)
+ {
+ if (!isRunning || currentAction == null)
+ return;
+
+ currentActionTime += deltaTime;
+
+ // 调用更新回调
+ currentAction.OnUpdate?.Invoke(deltaTime);
+
+ // 检查是否完成
+ bool isComplete = false;
+ if (currentAction.UsesDuration)
+ {
+ isComplete = currentActionTime >= currentAction.Duration;
+ }
+ else if (currentAction.IsComplete != null)
+ {
+ isComplete = currentAction.IsComplete();
+ }
+
+ if (isComplete)
+ {
+ // 当前Action完成
+ currentAction.OnEnd?.Invoke();
+ currentAction = null;
+ currentActionTime = 0f;
+
+ // 开始下一个Action
+ StartNextAction();
+ }
+ }
+
+ private void StartNextAction()
+ {
+ if (actionQueue.Count == 0)
+ {
+ // 队列为空,停止
+ isRunning = false;
+ return;
+ }
+
+ currentAction = actionQueue.Dequeue();
+ currentActionTime = 0f;
+ currentAction.OnStart?.Invoke();
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Assets/CapabilitySystem/Core/ActionQueue.cs.meta b/Assets/CapabilitySystem/Core/ActionQueue.cs.meta
new file mode 100644
index 0000000..884e767
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/ActionQueue.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 590dcae1712665a49ae5ffced1edce04
\ No newline at end of file
diff --git a/Assets/CapabilitySystem/Core/Capability.cs b/Assets/CapabilitySystem/Core/Capability.cs
new file mode 100644
index 0000000..68c4210
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/Capability.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace CapabilitySystem
+{
+ ///
+ /// Capability基类
+ /// Capability是一个单一的功能单元,包含自身所有的状态和决策逻辑
+ ///
+ public abstract class Capability
+ {
+ #region Properties
+
+ /// 是否激活
+ public bool IsActive { get; private set; }
+
+ /// 拥有者GameObject
+ public GameObject Owner { get; private set; }
+
+ /// Tick组
+ public TickGroup TickGroup { get; private set; }
+
+ /// Tick顺序
+ public int TickOrder { get; private set; }
+
+ /// 标签列表
+ public List Tags { get; private set; }
+
+ /// 配置数据
+ public CapabilityData Data { get; private set; }
+
+ /// 关联的CapabilityComponent
+ protected CapabilityComponent CapabilityComponent { get; private set; }
+
+ /// 黑板系统(快捷访问)
+ protected CapabilityBlackboard Blackboard => CapabilityComponent?.Blackboard;
+
+ /// 时间记录器(快捷访问)
+ protected TemporalLogger TemporalLogger => CapabilityComponent?.TemporalLogger;
+
+ /// 激活历史记录(用于调试)
+ public List ActivationHistory { get; private set; }
+
+ /// 最大历史记录数量
+ private const int MaxHistoryCount = 500; // 增加到500条,支持更长时间的调试
+
+ /// IsBlocked缓存
+ private bool cachedIsBlocked;
+ private int lastBlockCheckFrame = -1;
+
+ #endregion
+
+ #region Lifecycle
+
+ ///
+ /// 初始化Capability
+ ///
+ internal void Initialize(GameObject owner, CapabilityComponent component, CapabilityData data = null)
+ {
+ Owner = owner;
+ CapabilityComponent = component;
+ Data = data;
+ Tags = new List();
+ IsActive = false;
+ ActivationHistory = new List();
+
+ // 读取TickOrder特性
+ var attribute = GetType().GetCustomAttributes(typeof(TickOrderAttribute), true);
+ if (attribute.Length > 0)
+ {
+ var tickOrderAttr = (TickOrderAttribute)attribute[0];
+ TickGroup = tickOrderAttr.TickGroup;
+ TickOrder = tickOrderAttr.Order;
+ }
+ else
+ {
+ // 默认值
+ TickGroup = TickGroup.Gameplay;
+ TickOrder = 0;
+ }
+
+ // 注册到系统
+ CapabilitySystem.Instance.RegisterCapability(this);
+
+ // 调用子类Setup
+ Setup();
+ }
+
+ ///
+ /// 销毁Capability
+ ///
+ internal void Destroy()
+ {
+ if (IsActive)
+ {
+ Deactivate();
+ }
+
+ CapabilitySystem.Instance.UnregisterCapability(this);
+ }
+
+ ///
+ /// 激活Capability
+ ///
+ internal void Activate()
+ {
+ if (IsActive) return;
+
+ IsActive = true;
+
+ // 记录激活时间
+ var record = new CapabilityActivationRecord
+ {
+ ActivateTime = Time.time,
+ ActivateFrame = Time.frameCount,
+ Position = Owner.transform.position
+ };
+ ActivationHistory.Add(record);
+
+ // 限制历史记录数量
+ if (ActivationHistory.Count > MaxHistoryCount)
+ {
+ ActivationHistory.RemoveAt(0);
+ }
+
+ // 记录到TemporalLogger
+ if (TemporalLogger != null)
+ {
+ string key = $"{Owner.name}.{GetType().Name}";
+ TemporalLogger.Log($"{key}.Active", true);
+ TemporalLogger.LogPosition(key, Owner.transform.position);
+ TemporalLogger.LogRotation(key, Owner.transform.rotation);
+ }
+
+ OnActivated();
+ }
+
+ ///
+ /// 失活Capability
+ ///
+ internal void Deactivate()
+ {
+ if (!IsActive) return;
+
+ IsActive = false;
+
+ // 记录失活时间
+ if (ActivationHistory.Count > 0)
+ {
+ var lastRecord = ActivationHistory[ActivationHistory.Count - 1];
+ lastRecord.DeactivateTime = Time.time;
+ lastRecord.DeactivateFrame = Time.frameCount;
+ lastRecord.Duration = lastRecord.DeactivateTime - lastRecord.ActivateTime;
+ }
+
+ // 记录到TemporalLogger
+ if (TemporalLogger != null)
+ {
+ string key = $"{Owner.name}.{GetType().Name}";
+ TemporalLogger.Log($"{key}.Active", false);
+ }
+
+ OnDeactivated();
+ }
+
+ ///
+ /// 每帧更新(仅在激活时调用)
+ ///
+ internal void Tick(float deltaTime)
+ {
+ TickActive(deltaTime);
+ }
+
+ #endregion
+
+ #region Virtual Methods (子类重写)
+
+ ///
+ /// 初始化时调用,用于获取组件引用等
+ ///
+ protected virtual void Setup() { }
+
+ ///
+ /// 检查是否应该激活(未激活时每帧调用)
+ ///
+ protected virtual bool ShouldActivate() { return false; }
+
+ ///
+ /// 检查是否应该失活(激活时每帧调用)
+ ///
+ protected virtual bool ShouldDeactivate() { return false; }
+
+ ///
+ /// 激活时调用
+ ///
+ protected virtual void OnActivated() { }
+
+ ///
+ /// 失活时调用
+ ///
+ protected virtual void OnDeactivated() { }
+
+ ///
+ /// 激活状态下每帧调用
+ ///
+ protected virtual void TickActive(float deltaTime) { }
+
+ #endregion
+
+ #region Tag Blocking
+
+ ///
+ /// 阻塞带有指定Tag的所有Capability
+ ///
+ protected void BlockCapabilitiesWithTag(CapabilityTag tag, Instigator instigator)
+ {
+ CapabilityComponent.BlockTag(tag, instigator);
+ }
+
+ ///
+ /// 解除阻塞带有指定Tag的Capability
+ ///
+ protected void UnblockCapabilitiesWithTag(CapabilityTag tag, Instigator instigator)
+ {
+ CapabilityComponent.UnblockTag(tag, instigator);
+ }
+
+ ///
+ /// 检查是否被阻塞(带缓存优化)
+ ///
+ public bool IsBlocked()
+ {
+ // 同一帧内使用缓存结果
+ if (lastBlockCheckFrame == Time.frameCount)
+ {
+ return cachedIsBlocked;
+ }
+
+ cachedIsBlocked = false;
+ foreach (var tag in Tags)
+ {
+ if (CapabilityComponent.IsTagBlocked(tag))
+ {
+ cachedIsBlocked = true;
+ break;
+ }
+ }
+
+ lastBlockCheckFrame = Time.frameCount;
+ return cachedIsBlocked;
+ }
+
+ #endregion
+
+ #region Dynamic Priority
+
+ ///
+ /// 运行时设置Tick顺序
+ ///
+ public void SetTickOrder(TickGroup group, int order)
+ {
+ if (TickGroup != group || TickOrder != order)
+ {
+ TickGroup = group;
+ TickOrder = order;
+ CapabilitySystem.Instance.MarkNeedsSort();
+ }
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ ///
+ /// 获取组件(泛型方法)
+ ///
+ protected T GetComponent() where T : Component
+ {
+ return Owner.GetComponent();
+ }
+
+ ///
+ /// 尝试获取组件
+ ///
+ protected bool TryGetComponent(out T component) where T : Component
+ {
+ return Owner.TryGetComponent(out component);
+ }
+
+ #endregion
+
+ #region Internal Update Methods
+
+ ///
+ /// 内部更新方法,由CapabilitySystem调用
+ ///
+ internal bool InternalShouldActivate()
+ {
+ if (IsBlocked()) return false;
+ return ShouldActivate();
+ }
+
+ ///
+ /// 内部失活检查,由CapabilitySystem调用
+ ///
+ internal bool InternalShouldDeactivate()
+ {
+ return ShouldDeactivate();
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/CapabilitySystem/Core/Capability.cs.meta b/Assets/CapabilitySystem/Core/Capability.cs.meta
new file mode 100644
index 0000000..d75f412
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/Capability.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4dbadc84377efab488b761cd541fc9b6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/CapabilitySystem/Core/CapabilityActivationRecord.cs b/Assets/CapabilitySystem/Core/CapabilityActivationRecord.cs
new file mode 100644
index 0000000..df6963c
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/CapabilityActivationRecord.cs
@@ -0,0 +1,41 @@
+namespace CapabilitySystem
+{
+ ///
+ /// Capability激活记录
+ /// 用于调试和时间轴显示
+ ///
+ public class CapabilityActivationRecord
+ {
+ /// 激活时间
+ public float ActivateTime { get; set; }
+
+ /// 失活时间
+ public float DeactivateTime { get; set; }
+
+ /// 激活帧
+ public int ActivateFrame { get; set; }
+
+ /// 失活帧
+ public int DeactivateFrame { get; set; }
+
+ /// 持续时间
+ public float Duration { get; set; }
+
+ /// 是否仍在激活中
+ public bool IsStillActive => DeactivateTime == 0;
+
+ /// 阻塞原因(如果有)
+ public string BlockReason { get; set; }
+
+ /// 激活时的位置(可选)
+ public UnityEngine.Vector3? Position { get; set; }
+
+ /// 自定义数据(用于扩展)
+ public System.Collections.Generic.Dictionary CustomData { get; set; }
+
+ public CapabilityActivationRecord()
+ {
+ CustomData = new System.Collections.Generic.Dictionary();
+ }
+ }
+}
diff --git a/Assets/CapabilitySystem/Core/CapabilityActivationRecord.cs.meta b/Assets/CapabilitySystem/Core/CapabilityActivationRecord.cs.meta
new file mode 100644
index 0000000..819bb25
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/CapabilityActivationRecord.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 504183dddb5845f4fb0aa0beedf7302a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/CapabilitySystem/Core/CapabilityBlackboard.cs b/Assets/CapabilitySystem/Core/CapabilityBlackboard.cs
new file mode 100644
index 0000000..1e19ccc
--- /dev/null
+++ b/Assets/CapabilitySystem/Core/CapabilityBlackboard.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace CapabilitySystem
+{
+ ///
+ /// Capability黑板系统
+ /// 用于Capability之间共享数据和通信
+ ///
+ public class CapabilityBlackboard
+ {
+ private Dictionary data = new Dictionary();
+ private Dictionary>> listeners = new Dictionary>>();
+
+ #region Data Access
+
+ ///
+ /// 设置数据
+ ///
+ public void Set(string key, T value)
+ {
+ if (string.IsNullOrEmpty(key))
+ {
+ Debug.LogWarning("[CapabilityBlackboard] Key cannot be null or empty");
+ return;
+ }
+
+ data[key] = value;
+
+ // 通知监听者
+ if (listeners.ContainsKey(key))
+ {
+ foreach (var listener in listeners[key])
+ {
+ listener?.Invoke(value);
+ }
+ }
+ }
+
+ ///
+ /// 获取数据
+ ///
+ public T Get(string key, T defaultValue = default)
+ {
+ if (string.IsNullOrEmpty(key))
+ {
+ Debug.LogWarning("[CapabilityBlackboard] Key cannot be null or empty");
+ return defaultValue;
+ }
+
+ if (data.TryGetValue(key, out object value))
+ {
+ if (value is T typedValue)
+ {
+ return typedValue;
+ }
+ else
+ {
+ Debug.LogWarning($"[CapabilityBlackboard] Type mismatch for key '{key}'. Expected {typeof(T)}, got {value.GetType()}");
+ return defaultValue;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ ///
+ /// 尝试获取数据
+ ///
+ public bool TryGet(string key, out T value)
+ {
+ value = default;
+
+ if (string.IsNullOrEmpty(key))
+ return false;
+
+ if (data.TryGetValue(key, out object objValue) && objValue is T typedValue)
+ {
+ value = typedValue;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 检查是否包含指定键
+ ///
+ public bool Contains(string key)
+ {
+ return !string.IsNullOrEmpty(key) && data.ContainsKey(key);
+ }
+
+ ///
+ /// 移除数据
+ ///
+ public bool Remove(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ return false;
+
+ return data.Remove(key);
+ }
+
+ ///
+ /// 清空所有数据
+ ///
+ public void Clear()
+ {
+ data.Clear();
+ }
+
+ #endregion
+
+ #region Event System
+
+ ///
+ /// 监听数据变化
+ ///
+ public void AddListener(string key, Action