MonoFSM
SkillDev toolsUsage guide for the MonoFSM finite state machine framework. Use when you need to: (1) understand MonoFSM architecture and design philosophy (2) add/modify States, Transitions, Conditions, or Actions in a Unity Scene (3) write new Action or Condition C# scripts (4) use the Auto series Attributes for
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the MonoFSM skill
What this skill tells your AI
The instructions your AI receives, as published by red-candle-games-co-ltd/monofsm in skills/MonoFSM/SKILL.md and read by ahel’s review.
以 GameObject 層級為核心的有限狀態機框架。
核心設計
GameObject 層級表達式:狀態、轉換、動作和條件都是場景中的 GameObject。
[FSM Root] # MonoFSMOwner
├── [VarFolder] VariableFolder # 變數區(VariableFolder + StateMachineLogic)
│ └── f_varName # VarFloat / VarEntity 等
│ └── BoundModifier # VariableFloatBoundModifier(可選)
├── [SchemaFolder] SchemaFolder
├── [StateFolder] StateFolder # StateMachineLogic 主體
│ └── [State] StateName # GeneralState
│ ├── OnStateEnterHandler # 進入時觸發 → 子層放 Action
│ │ └── [Action] XxxAction # AbstractStateAction 實作
│ ├── [Timer] TimerName # VarFloatCountDownTimer(可選)
│ └── [Transition] => Target # TransitionBehaviour
│ └── [Condition] Name # AbstractConditionBehaviour 實作
└── Context # MonoContext
場景編輯(Unity MCP)
直接用 MCP 工具在 Scene 中編輯 FSM,見 references/scene-editing.md。
程式化讀取 FSM 結構
實作 Editor 工具(匯出、視覺化、批次修改)需要 traverse MonoFSM 階層時,見 references/fsm-traversal.md。涵蓋 StateFolder 偵測、變數/狀態/轉換/條件/動作的走訪規則,以及 AnimatorPlayAction 不繼承 AbstractStateAction 等 gotcha。實作範本:MonoFSM/1_MonoFSM_Core/Editor/PrefabExporter/FsmTextExporter.cs。
Auto Attributes
[Auto] // GetComponent<T>()
[AutoParent] // GetComponentInParent<T>()
[AutoChildren] // GetComponentsInChildren<T>()
[AutoChildren(DepthOneOnly = true)] // 僅直接子物件
欄位型別可以是 interface([AutoParent] private ICurrentEntityOwner _owner;),底層走 GetComponentInParent(Type, true),取到的是最近的一顆 parent,可用來當「多種容器共用同一個 child 元件」的自動接線。
Editor 下 Auto 欄位還沒解析:用 AutoReferenceFieldEditor
Auto 系列在 editor 下要等 Inspector 被點開才會解析,所以 Description、IsValid 這類「畫 hierarchy 就會被呼叫」的成員裡,Auto 欄位常常還是 null → NRE 或一直噴 error。不要自己寫 GetComponentInParent 補,也不要用 if (Application.isPlaying) 迴避,要當場補解析:
[ShowInInspector] [AutoParent] private ICurrentEntityOwner _owner;
private ICurrentEntityOwner Owner
{
get
{
if (_owner == null)
AutoAttributeManager.AutoReferenceFieldEditor(this, nameof(_owner));
return _owner;
}
}
AutoAttributeManager在 global namespace,不用 using;方法標了[Conditional("UNITY_EDITOR")],build 時整個 call site 被移除,內部又自己if (Application.isPlaying) return,runtime 零成本- 走的是同一顆 attribute 的
Execute,LimitedType/ includeSelf 等設定都會被尊重,不會把語意寫死 - 反射結果進
FieldCache,比每次GetComponentInParent便宜 - 即使補了解析仍可能是 null(真的沒接),呼叫端還是要 null guard;error log 只在
Application.isPlaying時才印,否則 editor 會刷滿 console - 既有範例:
AbstractMonoVariable.HasParentVarEntity、MonoEntity._fsmLogic、MonoBlackboard的各 folder、ValueProvider._parentEntity、VarEntityCurrentItem.Owner
其他常用 Attributes
[Required] // 必填欄位(Inspector 警告)
[CompRef] // 標記為組件引用
[DropDownRef] // 下拉選擇(需手動在 Inspector 設定,MCP 無法設定此類型)
[SOConfig("子資料夾名")] // ScriptableObject 欄位用,提供 Create 按鈕與路徑選擇器
SOConfig 注意事項
[SOConfig] 的 Drawer(SOConfigAttributeDrawer)使用 IList.Add() 新增資產,因此:
- 集合欄位必須用
List<T>,不可用T[](原生陣列大小固定,Add()會拋NotSupportedException) - 範例:
[SerializeField] [SOConfig("StateTags")] private List<StateTag> _stateTags = new();
狀態優先級
狀態有 Priority 屬性,高優先級狀態不會被低優先級狀態打斷。
狀態進入條件
優先把「能否進入此 State」的條件放在目標 State 的 CanEnterState。
不要把相同條件分散複製到各個來源 State 的 Transition;這樣多個 State 要轉入同一目標時,只需各自建立轉向該 State 的 Transition,進入資格仍由目標 State 統一維護。
只有條件確實取決於「從哪個來源 State 離開」時,才放在該來源的 Transition。
命名規範
SerializeField和public field以底線開頭:_myField- 百分比/比例欄位使用 0~1 範圍(
[Range(0f, 1f)]),不用 0~100 - 新寫的 component(繼承鏈上有
AbstractDescriptionBehaviour)一律 overrideDescription,把關鍵欄位組成一句話,hierarchy / State 樹才看得懂。細節與陷阱見 references/writing-actions.md
常用組件清單
EffectDealer / EffectReceiver 系統
定義「誰可以對誰造成效果」的互動系統,見 references/effect-system.md。
物件(receiver 端)要讀「誰在跟我互動」身上的值時,走 best match 的 EffectEnterBestMatchNode._hittingEntity,不要取本機玩家;組法與坑見 effect-system.md 的「在物件上取『誰在跟我互動』的 selector entity」。
新增 detector 時放現成的 MonoFSM/0_MonoFSM_Example_Module/[Detector] Trigger.prefab,不要手刻——手刻容易漏掉 kinematic Rigidbody,static-static 的 trigger 完全不觸發且沒有錯誤訊息。細節見 effect-system.md 的「新增 EffectDetector」。
ValueSource / Variable 系統
AbstractValueSource<T> 泛型基類用於每幀計算並提供值(方向、位置、輸入等)。Variable 系統(VarFloat、VarVector3 等)的 IsValueExist 用於判斷 runtime 有效值。詳見 references/value-source.md。
需要「目標位置」時,用 TargetPositionResolver(namespace MonoValueProvider,在 Core),不要在欄位寫死 Transform。它是 [Serializable],統一解析 VarVector3 / VarTransform / VarEntity 三種來源(優先序:Vector3 > Transform > Entity,各自 IsValueExist 才採用)。常用 API:GetTargetPosition(fallback)、ResolvedTransform、HasTarget、ActiveSource、ClearPositionTarget()。用法:欄位宣告 [InlineProperty][HideLabel] public TargetPositionResolver _source = new();,取值前先判 HasTarget。位置:1_MonoFSM_Core/Runtime/0_Pattern/DataProvider/EntityProvider/ValueSource/TargetPositionResolver.cs。
VarWrapper 系列(可綁 Var 或填常數的欄位)
VarFloatWrapper / VarIntWrapper 等 [Serializable] 包裝類,讓欄位在 Inspector 二選一:綁一個 Var 引用,或直接填常數。取值一律用 .Value,宣告預設值用 new(...)(如 private VarIntWrapper _index = new(-1)),namespace 為 MonoFSM.Variable。數值參數欄位一律用 Wrapper,不要並排寫 float _x + VarFloat _xVar。詳見 references/var-wrapper.md。
C# 效能模式
撰寫 MonoFSM 相關 C# 程式碼時的 GC 避免技巧,見 references/csharp-patterns.md。
Serialized 欄位型別遷移
需要把已序列化的欄位改成不同型別(如 VarFloat 直接參照 → VarFloatWrapper)又不想掉 prefab reference 時,見 references/serialization-migration.md。涵蓋為何直接改型別一定掉 ref、legacy 欄位 + FormerlySerializedAs 接舊資料、LoadPrefabContents 批次遷移、驗證與清孤兒資料的完整 6 步流程。
References
| 檔案 | 什麼情況要讀它 |
|---|---|
| references/writing-actions.md | 要新寫或修改 Action / Condition 的 C# 腳本時。含 Action / Condition 範本、Description override 慣例、Render behaviour 掛載位置決定觸發時機(多人時 client 跑不跑)、同一功能要同時支援 Action 與 Render 的 Writer 拆法 |
| references/design-patterns.md | 設計一個新機制、或既有機制會漏狀態/時序出錯時。含 Data-Driven(用 Var 當溝通介面)、持續性狀態改用拉式 Getter + Switch Simulate、Unity 回調 cache 到 Simulate 統一處理、Raycast 一律走 IRaycastProcessor |
| references/scene-editing.md | 要在 Unity Scene / prefab 上實際新增或修改 State、Transition、Condition、Action 節點時 |
| references/fsm-traversal.md | 寫 Editor 工具要程式化走訪 FSM 階層(匯出、視覺化、批次修改)時 |
| references/components.md | 想知道有哪些現成的 State / Action / Condition / Timer 等組件可以直接用,不用自己寫時 |
| references/effect-system.md | 處理 EffectDealer / EffectReceiver 互動(誰能對誰造成效果、偵測、判定)時;也含「物件上要取互動者(selector)entity」的組法、新增 EffectDetector 該放哪顆 nested prefab 與 kinematic Rigidbody 的坑 |
| references/value-source.md | 要做每幀計算並提供值的 AbstractValueSource<T>,或需要理解 Variable 的 IsValueExist / getter 型 IsNull 語意、runtime 寫入的 Var 要勾 _isRuntimeOnly、或 Var 底下要掛多顆 condition(只看第一顆 active 的,不是 OR,要 AND 得包 CompositeCondition)時 |
| references/var-wrapper.md | 欄位要讓使用者在「綁一個 Var」與「直接填常數」之間二選一(VarFloatWrapper 等)時 |
| references/csharp-patterns.md | 寫每幀執行的程式碼、需要避免 GC 配置時;也含序列化 array 欄位不會是 null 導致 ??= lazy init 失效的坑 |
| references/serialization-migration.md | 要改已序列化欄位的型別又不想掉 prefab reference 時 |
Signals
- GitHub stars
- 25
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
monofsm- Source
- github.com/red-candle-games-co-ltd/monofsm