ホーム/ゲーム開発/msw-behaviourtree
M

msw-behaviourtree

by @msw-gitv
4.4(120)

この Skill は、MSW の `.behaviourtree` ファイルをエンドツーエンドで作成し、プロジェクト固有のオーサリング仕様を維持します。コードブロックをスキャンしてカスタムビヘイビアノードのカタログを構築し、ノードグラフ、ブラックボード変数、プロパティ配線を含む完全なビヘイビアツリーを生成し、自己検証を行います。この Skill は、ゲーム AI のビヘイビアツリーの作成と管理を大幅に簡素化し、プロジェクト仕様の一貫性と正確性を保証します。

behaviour-treegame-aicode-generationgame-developmentautomationGitHub
インストール方法
npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-behaviourtree
compare_arrows

Before / After 効果比較

1
使用前

開発者が複雑なビヘイビアツリーを手動で作成および更新する際、カスタムノード、UUID、プロパティキー、バージョン情報を細かく確認する必要があり、エラーが発生しやすく時間がかかります。プロジェクト仕様の保守も手作業に依存するため、一貫性の欠如や潜在的な実行時問題につながります。

使用後

この Skill は、ビヘイビアツリーの生成と仕様保守を自動化し、コードブロックをスキャンしてノードカタログを自動的に構築し、完全で検証済みのビヘイビアツリーを生成します。これにより、手作業が大幅に削減され、データの一貫性が確保され、開発のイテレーションが加速されます。

SKILL.md

MSW BehaviourTree

End-to-end authoring skill for MSW .behaviourtree files. Owns both the project-specific authoring spec (<ProjectRoot>/.behaviourDocs/bt-spec.md) and the tree generation itself. Fixed graph rules and skeletons live in this skill's references/; the per-project spec is (re)built by this skill's local scripts/build-spec.cjs.


🚦 Execution order (follow this sequence)

0. Build / refresh the project spec (bt-spec.md)

The spec is the source of truth for every project-specific data point: each custom action/decorator node's definitionId, btNodeType, visible propertyKey names, and the serialized Type.type strings stamped to this project's CoreVersion.

When to (re)build:

  • First time working on BT in a project (no .behaviourDocs/bt-spec.md yet).
  • After any change that affects BT node surface area:
    • new / renamed / removed .codeblock whose paired .mlua extends ActionNode / DecoratorNode
    • added / removed / renamed property lines in such a .mlua
    • Environment/config CoreVersion bumped (the serialized type strings are version-tagged).
  • The user says they recently added/changed a BT codeblock or a .mlua property — stale UUIDs / missing properties silently produce broken trees.
  • The downstream validation (Step 7) flags a definitionId, propertyKey, or version mismatch.

How to run — invoke this skill's local script:

node "scripts/build-spec.cjs" --projectRoot "<MSW project root>"

If the current working directory is already the MSW project root, --projectRoot can be omitted. Requires Node.js on PATH (no other dependencies — pure stdlib fs/path).

Optional overrides (long flags, case-insensitive):

FlagDefaultNotes
--projectRootcurrent working directoryMSW project root to scan
--outputPath<ProjectRoot>/.behaviourDocs/bt-spec.mdfolder is created if missing
--coreVersionread from <ProjectRoot>/Environment/config (CoreVersion field)required if the config is missing

Example with overrides:

node "scripts/build-spec.cjs" --projectRoot "C:/path/to/project" --coreVersion 26.5.0.0

The script throws if Environment/config is absent and --coreVersion is not passed — there is no fallback default.

What the spec contains:

  1. Project metadata — project root, CoreVersion, generated time, discovered node counts.
  2. Built-in composite node names and their fixed definitionId / btNodeType.
  3. Custom action nodes — Name, definitionId, btNodeType, visible property names.
  4. Custom decorator nodes — same shape as action nodes.
  5. Type map — mlua type to serialized MODNativeType.type plus Blackboard ObjectValue shape.

UUIDs come from real .codeblock files in the project — the spec never invents them. @HideFromInspector properties are filtered out automatically. Fixed authoring rules, file skeletons, and validation checklists live in this skill's references/ rather than in the generated spec.

After (re)building, read the freshly written <ProjectRoot>/.behaviourDocs/bt-spec.md and continue with the steps below. The compact spec intentionally lists only property names; when constructing nodeProperties, resolve each property's mlua type/default from the paired .mlua file, then use the type map in bt-spec.md §4 for propertyType.type.

Also read references/skeleton-minimal.json for the smallest valid tree, references/skeleton-full.json for a Composite+Decorator+Action+Blackboard example with all optional fields populated, references/node-catalog.md for fixed graph rules, and any existing .behaviourtree in the project (**/*.behaviourtree) to mirror conventions. Replace {CORE_VERSION} in the skeletons with the CoreVersion from bt-spec.md — both at the top level and inside every MOD.Core.* type string in Blackboard variables and nodeProperties.

1. Collect input from the user

Confirm via context, or ask via AskUserQuestion if anything is ambiguous:

ItemDescriptionExample
nameDisplay name for the tree"PatrolAndChase"
Save path.behaviourtree location (relative to project root)RootDesk/MyDesk/PatrolAndChase.behaviourtree
Tree shapeIntended node graph (root composite + children)Sequence → [Chase, MoveTo]
Custom nodesAction/decorator codeblocks the tree referencesChase, MoveTo, Jump
Blackboard variablesVariable name + type + initial valueTargetEntity: Entity, MoveSpeed: number = 10.0
Node propertiesFor each custom node, which property maps to which Blackboard variableChase.TargetEntityKey = "TargetEntity"

Custom-node existence check (mandatory): every custom action/decorator name the user mentions must appear in bt-spec.md §2 / §3. If a referenced node is not in the spec, stop and ask the user — do not invent a UUID, do not assume a node exists by name, and do not skip rerunning Step 0.

2. Mint UUIDs

You need:

  • One UUID for the file → goes into EntryKey and ContentProto.Json.id (both identical, both prefixed behaviourtree://).
  • One UUID for each node in Nodes (nodeId).
node -e "console.log(require('node:crypto').randomUUID())"

Mint up front, write into a scratch table, then assemble. Don't reuse the file UUID as a nodeId.

3. Resolve every definitionId

Node categorydefinitionId valuebtNodeType
Built-in composite (SequenceNode, SelectorNode, ParallelNode)Same string as nodeName1
Custom action nodevalue from bt-spec.md §20
Custom decorator nodevalue from bt-spec.md §32

Custom-node UUIDs come from bt-spec.md (which read them from real .codeblock files) — never any other source.

4. Build the Blackboard

For each variable, copy the Type.type string and ObjectValue shape verbatim from bt-spec.md §4. The version-tagged substring (Version=<CoreVersion>) must match exactly — a typo silently breaks deserialization.

Variables is an ordered array; each entry: { Name, Type: { "$type": "MODNativeType", type: "<from spec>" }, ObjectValue: <from spec> }. The ObjectValue does not include a $type discriminator (unlike Value in .model files).

For Component / ComponentRef, ComponentId is <entity-uuid>:<ComponentName> (engine component) or <entity-uuid>:<scriptCodeblockUuid>:<ScriptComponentName> (script component). Mirror an existing serialized example in the project.

Numeric ObjectValues use float literal form (3.0, not 3).

4.5 Resolve node property values

For each custom node that needs nodeProperties:

  1. Confirm the propertyKey exists in bt-spec.md §2 / §3 for that node.
  2. Find the paired .mlua by searching for script <NodeName> extends ActionNode or script <NodeName> extends DecoratorNode under the project. If multiple files match, prefer the one whose sibling .codeblock has the exact definitionId UUID from bt-spec.md; if still ambiguous, ask the user.
  3. Read the visible property declarations in that .mlua, ignoring @HideFromInspector properties. This gives the mlua type and default value.
  4. Include a nodeProperties entry only when the user provided a value, the behavior requires a non-default value, or a *Key property must point at a Blackboard variable. Omit optional properties that can safely use the .mlua default.
  5. For *Key string properties, set propertyValue to the Blackboard variable name. Infer the variable by name and getter usage when obvious (MoveSpeedKey -> MoveSpeed, TargetEntityKey -> TargetEntity). If more than one Blackboard variable could match, ask.
  6. For literal properties, use the user-provided value. If no value is provided and the .mlua default is meaningful, omit the property instead of serializing a guessed value.
  7. If OnBehave checks a property for nil, empty string, or invalid enum and no value can be inferred, ask the user before writing the tree.

nodeProperties entry shape:

{
  "propertyKey": "<property name>",
  "propertyType": { "$type": "MODNativeType", "type": "<type from bt-spec.md §4>" },
  "propertyValue": <value>
}

5. Assemble Nodes

Hard graph constraints (validate before writing):

  • RootNode is not a parent node. It must not have childNodes. It only stores startNodeId, and startNodeId points to exactly one node in Nodes.
  • If the tree needs several top-level behaviors, use either one Composite as the single startNodeId, or one Decorator as the single startNodeId whose decoChildNodes wraps a Composite or another Decorator chain that eventually wraps a Composite. Put the multiple behaviors under that Composite's childNodes.
  • Exactly one node in Nodes may have nodeParentId: "": the node referenced by RootNode.startNodeId. Do not create multiple root-level Action/Composite/Decorator nodes.
  • Composite (btNodeType: 1) is the only node category that can own multiple children through childNodes.
  • Decorator (btNodeType: 2) is only a wrapper/parent for exactly one Action, Composite, or Decorator node. It can also be the child of another Decorator, so Decorator-to-Decorator chains are valid. It must use singular decoChildNodes (a single nodeId string) for that one child, not childNodes; the wrapped child must also record the Decorator's id in its nodeParentId.
  • Decorators applying to the same Action MUST be chained — never flattened as siblings. Each decorator owns exactly one downstream subtree. If two or more decorators are meant to gate/modify the same Action, build a single chain Composite → ADeco → BDeco → CDeco → Action where each decorator's decoChildNodes points to the next decorator (and finally the Action). Concretely: within one chain leading to a single Action, no two decorators may share the same nodeParentId — each decorator's parent is the previous decorator, and only the topmost decorator's parent is the Composite. Sibling decorators under one Composite are still valid when each wraps a different downstream subtree. ✅ Composite → ADeco → BDeco → CDeco → Action (chain — every decorator has a unique parent within the chain). ❌ Composite → [ADeco→Action, BDeco→Action, CDeco→Action] (Action duplicated to bypass chaining). ❌ Composite → [ADeco, BDeco, CDeco, Action] (decorators flattened — they don't wrap the Action and are effectively orphaned).
  • Action (btNodeType: 0) is a leaf — never has children.

Node-write invariants:

  • Every nodeId is unique within the file.
  • nodeParentId of every non-root node points to a real nodeId that is a Composite or Decorator. It must never point to RootNode, because RootNode is not represented as a node in Nodes.
  • If a node's parent is a Composite, that Composite must include the node id in childNodes.
  • If a node's parent is a Decorator, that Decorator's decoChildNodes must equal that node's nodeId. This is valid even when both parent and child are Decorators.
  • Composite childNodes ↔ child nodeParentId is bidirectionally consistent.
  • Action nodes omit childNodes. Decorator nodes omit childNodes and use exactly one decoChildNodes (single string nodeId) instead.
  • Never write probability. The editor strips this field on round-trip, and the supported composites (SequenceNode, SelectorNode, ParallelNode) do not consume per-child weights. Older generated trees in the project may still carry "probability": 1.0 on every node; treat that as legacy on read but do not write it on new nodes.
  • Decorator nodes (btNodeType: 2) omit nodePosition. The editor positions a Decorator automatically relative to the child it wraps, and writes no nodePosition field for it on save. Only Composites and Actions carry nodePosition. The RootNode block also carries its own nodePosition (separate from the start node).
  • Empty collection fields are omitted, not serialized as []. A Composite with no children yet should omit childNodes entirely; a node with no overrides should omit nodeProperties entirely. Empty arrays are an editor-draft artifact — do not author them.
  • Decorator child field is decoChildNodes (canonical — this is what the editor preserves on save; ChildNodeId is silently stripped on round-trip). It is a single string holding the wrapped child's nodeId (not an array). When reading legacy files you may still encounter ChildNodeId on hand-authored decorators; treat it as the same field. When writing, always emit decoChildNodes.
  • RootNode.startNodeId references one of the nodeIds — an Action, Composite, or Decorator — and that node is the only node with nodeParentId: "".

*Key-suffix String properties carry the name of a Blackboard variable (resolved at runtime via BlackBoard:GetXxx). Non-Key properties carry the literal value.

6. nodePosition format

nodePosition is a JSON object with numeric x / y:

"nodePosition": { "x": 0.0, "y": 0.0 }

Use float literals (0.0, not 0). The legacy string form "(0.000, 0.000)" may still appear in older hand-authored trees — read it as equivalent, but always write the object form (the BT editor canonicalizes to this shape on save, so the string form re-serializes to a noisy diff the first time the file is opened).

Editor axes: the BT editor uses a math-convention canvas — +x is right, +y is up (upper-right quadrant is positive). So a child placed at a higher y than its parent appears above the parent on screen.

Layout rule — draw the tree downward: depth grows along −y (children sit below their parent), and siblings spread along ±x around the parent's x. Typical spacing: 200 units between depth levels and 200 units between siblings.

RootNode block vs the start node — do not stack them at the same position. RootNode.nodePosition is the canvas anchor and stays at { "x": 0.0, "y": 0.0 }. The start node (the node referenced by startNodeId) must sit one level below that anchor — putting it at (0, 0) makes it visually overlap the RootNode marker on the editor canvas. Treat the RootNode anchor as depth 0 and the start node as depth 1.

  • RootNode.nodePosition: { "x": 0.0, "y": 0.0 } (fixed anchor — never moves)
  • Start node (depth 1, referenced by startNodeId): { "x": 0.0, "y": -200.0 }
  • Single child of the start node (depth 2): { "x": 0.0, "y": -400.0 }
  • Two children of the start node (depth 2): { "x": -100.0, "y": -400.0 } and { "x": 100.0, "y": -400.0 }
  • Each additional level: parent.y − 200

Never place a child at a y greater than or equal to its parent's y — that draws upward

...

ユーザーレビュー (0)

レビューを書く

効果
使いやすさ
ドキュメント
互換性

レビューなし

統計データ

インストール数2.6K
評価4.4 / 5.0
バージョン
更新日2026年7月10日
比較事例1 件

ユーザー評価

4.4(120)
5
37%
4
43%
3
13%
2
5%
1
2%

この Skill を評価

0.0

対応プラットフォーム

🤖claude-code

タイムライン

作成2026年6月21日
最終更新2026年7月10日
🎁 Agent 知識カード
アンケート