msw-avatar
この Skill は、ゲームやシミュレーション環境におけるキャラクターの外見とアニメーションを管理します。17のスロットを持つコスチュームの装備や、アニメーションの状態マッピング、アクションの上書きなどに対応。プレイヤー、NPC、モンスターなど、様々なエンティティのキャラクター表現を柔軟かつ多様にカスタマイズし、動作を制御できます。
npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-avatarBefore / After 効果比較
1 组開発者は、リソースの RUID を手動で検索し、設定ファイル内の17の装備スロットと複雑なアニメーション状態マッピングを個別に編集する必要がありました。これは時間がかかり、エラーが発生しやすく、意図しないキャラクターの動作につながっていました。
この Skill は、装備スロットとアニメーション状態を管理するための明確なインターフェースを提供し、設定プロセスを大幅に簡素化します。手作業とエラーを減らし、開発者がキャラクターの外見とアクションを迅速かつ正確にカスタマイズできるようにします。
MSW Avatar (Costume · Animation)
An avatar is managed along two axes.
- Costume (appearance):
MOD.Core.CostumeManagerComponent— which items are equipped (17 slots). - Animation (motion):
AvatarStateAnimationComponent+AvatarRendererComponent— which state clip is played (14 default states + custom actions).
Edit workspace files directly, then call the refresh tool of msw-maker-mcp so the editor picks up the change.
This document covers costume (file-edit based) first, then animation (script based) at the end.
Workspace path rule: maps
./map/, UI./ui/, scripts and other assets./RootDesk/MyDesk/, global models such as DefaultPlayer/Player./Global/.
Where to edit, by target
| Target | File to edit | Notes |
|---|---|---|
| DefaultPlayer | ./Global/DefaultPlayer.model | Override CostumeManagerComponent properties via the Values array |
| Player (base) | ./Global/Player.model | Costume defaults are usually overridden in DefaultPlayer.model, not here |
| Entities placed in a map (NPC, monster, etc.) | ./map/{mapName}.map | The CostumeManagerComponent block inside that entity's jsonString.@components |
| Entities that reference a custom model only | The corresponding .model (e.g. under ./RootDesk/MyDesk/) | When the map has no inline component and the entity is bound only by modelId, edit the model side |
Read (equivalent to get): read the file above and inspect the CostumeManagerComponent-related fields / Values entries. If Maker MCP is connected, you can use get_component as a runtime/editor snapshot helper (see the msw-maker-mcp skill).
Apply (equivalent to set): write values into the file, then call refresh.
Applying changes: MCP refresh
After saving the file you must call the refresh tool of the msw-maker-mcp server to sync Maker and its visual state. (See the tool list in the msw-maker-mcp skill.)
RUID (resource unique ID)
The string written into a costume is an avatar item RUID (typically a 32-character hex string).
- Never guess or fabricate an RUID. Look it up with the
msw-searchskill — for the avatar RUID workflow (default body/head, item detail, render composition) see../msw-search/references/resource/avatar.md; for generic search see../msw-search/references/resource/search.md; for single-item detail see../msw-search/references/resource/detail.md. - The script API
SetEquip(MapleAvatarItemCategory, itemRUID)and the value stored in the editor/model are the same RUID string. Custom*Equipslots only accept a plain Guid. Any prefixed form — includingthumbnail://<ruid>— is silently rejected and the slot is left unequipped (no error, no warning). RUIDs returned bymsw-searchare already plain Guids; do not prepend a scheme. See themsw-sprite-ruidskill for the broader thumbnail / icon rule.
CostumeManagerComponent overview
Attached to entities that use an avatar (player, NPC, etc.). Equipment slots are exposed as 17 string properties named Custom*Equip, and from scripts you access them via GetEquip / SetEquip with the MapleAvatarItemCategory enum.
Other synced properties
| Property | Type | Description |
|---|---|---|
| UseCustomEquipOnly | boolean (default false) | When true, the user account's default costume is ignored and only costumes assigned via script/model are used. Important when you want to lock the appearance inside a world. |
| DefaultEquipUserId | string | Clones the equipment of the specified user, then applies custom equipment on top. Users who are not currently online can also be specified. If that user later changes equipment, the reflected appearance may change. |
| EquippedItems | read-only | Actual equipped info at runtime. Cannot be modified from script. |
17 slots ↔ property ↔ MapleAvatarItemCategory
The 17 equipment string fields of CostumeManagerComponent map to the engine enum MapleAvatarItemCategory as follows. (Enum definition: see Environment/NativeScripts/Enum/MapleAvatarItemCategory.d.mlua.)
| # | Component property (string RUID) | MapleAvatarItemCategory | Notes |
|---|---|---|---|
| 1 | CustomBodyEquip | Body (1) | Skin / body |
| 2 | CustomHairEquip | Hair (3) | Hair |
| 3 | CustomFaceEquip | Face (4) | Face / face shape |
| 4 | CustomCapEquip | Cap (5) | Hat |
| 5 | CustomCapeEquip | Cape (6) | Cape |
| 6 | CustomCoatEquip | Coat (7) | Coat (top) |
| 7 | CustomLongcoatEquip | Longcoat (9) | Longcoat — an item class that occupies both the top and bottom slots |
| 8 | CustomPantsEquip | Pants (10) | Bottom |
| 9 | CustomGloveEquip | Glove (8) | Gloves |
| 10 | CustomShoesEquip | Shoes (12) | Shoes |
| 11 | CustomOneHandedWeaponEquip | OneHandedWeapon (13) | One-handed weapon |
| 12 | CustomTwoHandedWeaponEquip | TwoHandedWeapon (14) | Two-handed weapon — occupies both the one-handed weapon slot and the sub-weapon slot |
| 13 | CustomSubWeaponEquip | SubWeapon (15) | Sub-weapon |
| 14 | CustomFaceAccessoryEquip | FaceAccessory (16) | Face accessory |
| 15 | CustomEyeAccessoryEquip | EyeAccessory (17) | Eye accessory |
| 16 | CustomEarAccessoryEquip | EarAccessory (18) | Ear accessory |
| 17 | CustomEarEquip | Ear (19) | Ear (body part) |
Enum values without a direct 17-field counterpart
| MapleAvatarItemCategory | Description |
|---|---|
| Head (2) | Close to "not used as equipment" — handled automatically to match the body color. There is no CustomHeadEquip field. |
| Invalid (0) | Used to detect error / undefined values. |
| Shield (11) | Per the enum comment, it uses the SubWeapon slot. In storage it is safest to treat it as mutually exclusive with CustomSubWeaponEquip. |
Mutual exclusion / slot occupancy rules (must understand)
-
Longcoat ↔ Coat + Pants
Longcoat is designed to occupy both the Coat and Pants slots. When equipping a longcoat, put the longcoat RUID inCustomLongcoatEquipand resolve the combination with coat/pants logically — normally when a longcoat is in use, leave coat/pants empty or avoid conflicting visuals. -
Two-handed weapon ↔ One-handed weapon + sub-weapon
TwoHandedWeapon uses both the one-handed weapon slot and the sub-weapon slot. When using a two-handed weapon, center onCustomTwoHandedWeaponEquipand make sure values are not also set for one-handed/sub-weapon — avoid double equipping. -
Shield ↔ Sub-weapon
Shield uses the sub-weapon slot. Do not expect another sub-weapon to coexist withCustomSubWeaponEquip. -
Empty string = unequip
Just likeSetEquip(category, "")in script, leaving the field as""in a file means the slot is unequipped.
DefaultPlayer.model — putting costume into Values
Add or modify an entry in the ContentProto.Json.Values array of ./Global/DefaultPlayer.model.
- TargetType:
"MOD.Core.CostumeManagerComponent" - Name: a property name from the table above (e.g.
CustomCapEquip,UseCustomEquipOnly) - ValueType: follow the same pattern as other
Valuesentries already inDefaultPlayer.model. Strings useSystem.String, mscorlib, ..., booleans useSystem.Boolean, mscorlib, ... - Value: the RUID string or
true/false
If the same (TargetType, Name) already exists, update that entry only; otherwise append a new object to the array.
String slot example (structure only; replace the RUID via search)
{
"TargetType": "MOD.Core.CostumeManagerComponent",
"Name": "CustomCapEquip",
"ValueType": {
"$type": "MODNativeType",
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
},
"Value": "PUT_32_HEX_RUID_HERE"
}
UseCustomEquipOnly example
{
"TargetType": "MOD.Core.CostumeManagerComponent",
"Name": "UseCustomEquipOnly",
"ValueType": {
"$type": "MODNativeType",
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
},
"Value": true
}
Map entities — edit in the .map file
Open the entity record of the target map under ./map/.
- Find the target entity (by name/path/id) in the
ContentProto.Entitiesarray. - In
jsonString["@components"], find the object with"@type": "MOD.Core.CostumeManagerComponent". - Edit
Custom*Equip,UseCustomEquipOnly,DefaultEquipUserId, etc. on that object directly. - Confirm that
MOD.Core.CostumeManagerComponentis also listed in thecomponentNamesstring list, and that this list is consistent with the components array.
If the map uses binary-only format, the editing tool may differ depending on workspace policy. When the file opens as JSON text, follow the structure above.
Mapping GET /v3/avatars results to slots
Map the category field of an item returned by GET /v3/avatars to a Custom*Equip property. For the search method, see the msw-search skill → references/resource/avatar.md.
API category | Custom*Equip property | MapleAvatarItemCategory |
|---|---|---|
body | CustomBodyEquip | Body (1) |
hair | CustomHairEquip | Hair (3) |
face | CustomFaceEquip | Face (4) |
faceaccessory | CustomFaceAccessoryEquip | FaceAccessory (16) |
eyeaccessory | CustomEyeAccessoryEquip | EyeAccessory (17) |
earaccessory | CustomEarAccessoryEquip | EarAccessory (18) |
cap | CustomCapEquip | Cap (5) |
cape | CustomCapeEquip | Cape (6) |
longcoat | CustomLongcoatEquip | Longcoat (9) |
coat | CustomCoatEquip | Coat (7) |
pants | CustomPantsEquip | Pants (10) |
glove | CustomGloveEquip | Glove (8) |
shoes | CustomShoesEquip | Shoes (12) |
weapon | CustomOneHandedWeaponEquip | OneHandedWeapon (13) |
twohandweapon | CustomTwoHandedWeaponEquip | TwoHandedWeapon (14) |
subweapon | CustomSubWeaponEquip | SubWeapon (15) |
shield | CustomSubWeaponEquip | Shield (11) — shares the SubWeapon slot |
Avatar resource search reference
msw-searchskill →references/resource/avatar.md: details onGET /v3/avatars(costume search), default body/head,GET /v3/avatars/{ruid}, render composition, etc.- Combine the category search and detail API to collect equipment RUIDs.
Avatar tint / alpha (visual recoloring)
For color and transparency effects (hit flash, ghost fade, palette swap, etc.) on any entity that has AvatarRendererComponent attached — DefaultPlayer, avatar-bearing NPCs, monsters — use the renderer's own methods. SpriteRendererComponent.Color and FlipX are a silent no-op on an avatar entity (the avatar renderer paints over the sprite renderer's output even though isvalid(spriteRenderer) returns true).
| Method | Signature | Notes |
|---|---|---|
SetColor | (r, g, b, a [, targetUserId]) | r/g/b/a are floats in 0~1. Tints the whole avatar. Client ExecSpace. |
SetAlpha | (a [, targetUserId]) | Float in 0~1. Independent transparency. Client ExecSpace. |
SetAvatarPartColor | (category, r, g, b, a [, targetUserId]) | Tint only one MapleAvatarItemCategory slot. |
@ExecSpace("Client")
method void FlashRed()
local renderer = self.Entity.AvatarRendererComponent
if isvalid(renderer) == false then return end
renderer:SetColor(1.0, 0.25, 0.25, 1.0) -- red flash
wait(0.1)
renderer:SetColor(1.0, 1.0, 1.0, 1.0) -- restore
end
For avatar facing/flip, use the facing API on MovementComponent (e.g. MoveDirection) instead of writing the sprite-level flip — same silent-no-op reason.
Avatar animation — overall structure
Avatar animation flows through a 3-layer pipeline. Working on only one layer leads to the other layers overwriting your changes and producing unintended motions.
[1] Input / game logic
│ PlayerControllerComponent · scripts
▼
[2] StateComponent ──── StateChangeEvent ────▶ AvatarStateAnimationComponent
(e.g. "ATTACK") (CurrentStateName) (StateToAvatarBodyActionSheet
or ActionSheet lookup)
│
▼
[3] AvatarRendererComponent ◀── BodyActionStateChange / ActionStateChanged ── body entity
(actual sprite playback)
Key distinctions:
| Term | Format | Example |
|---|---|---|
| State key | UPPERCASE | IDLE, MOVE, ATTACK, HIT, CROUCH, FALL, JUMP, CLIMB, LADDER, DEAD, SIT, ATTACK_WAIT |
| AvatarBodyActionStateName (Value side) | lowercase | stand, walk, attack, hit, crouch, fall, rope, ladder, dead, sit, alert, fly, blink, heal |
| MapleAvatarBodyActionState (enum) | PascalCase | Stand, Walk, Attack, Hit, Crouch, Fall, Sit, Rope, Ladder, Dead, Blink, Fly, Heal, Alert, Invalid |
| CoreActionName / PartsActionName (actual sprite action ID) | lowercase + digits | stand1, walk1, swingO1, shoot1, prone, jump, alert, etc. |
Common confusion:
"attack"is not a State. The State is the uppercaseATTACK, the mapping Value is the lowercaseattack(=MapleAvatarBodyActionState.Attack), and that Value is then resolved into a sprite action ID such asswingO1/shoot1depending on the weapon. From script, the call that triggers the state isStateComponent:ChangeState("ATTACK")(UPPERCASE string) —"Attack"or"attack"silently misses (no error, the state simply does not change).
AvatarStateAnimationComponent — state ↔ motion mapping
MOD.Core.AvatarStateAnimationComponent holds both systems.
| Property | Used when | Type | Notes |
|---|---|---|---|
IsLegacy | Switch between the two systems | boolean (default false) | true = use ActionSheet, false = use StateToAvatarBodyActionSheet |
ActionSheet | IsLegacy = true (old) | SyncDictionary<string, string> | State→AnimationKey, e.g. "ATTACK" → "attack" |
StateToAvatarBodyActionSheet | IsLegacy = false (new, default) | SyncDictionary<string, AvatarBodyActionElement> | e.g. "ATTACK" → `{AvatarBodyActionStateName="a |
...
ユーザーレビュー (0)
レビューを書く
レビューなし
統計データ
ユーザー評価
この Skill を評価