M

msw-avatar

by @msw-gitv
4.4(120)

此技能用于管理游戏或模拟环境中的角色形象,包括服装穿戴(17个装备槽位)和动画行为(状态映射、动作覆盖)。它支持对玩家、NPC和怪物等各类实体进行精细的形象定制和动作控制,确保角色表现的灵活性和多样性。

avatarcostumeanimationgame-developmentcharacter-managementGitHub
安装方式
npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-avatar
compare_arrows

Before / After 效果对比

1
使用前

开发者需要手动查找资源 RUID,并逐一编辑配置文件中的 17 个装备槽位和复杂的动画状态映射,耗时且容易出错,导致角色表现不符合预期。

使用后

此技能提供清晰的装备槽位和动画状态管理接口,大幅简化了配置流程,减少了手动操作和错误,使开发者能快速准确地定制角色形象和动作。

SKILL.md

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

TargetFile to editNotes
DefaultPlayer./Global/DefaultPlayer.modelOverride CostumeManagerComponent properties via the Values array
Player (base)./Global/Player.modelCostume defaults are usually overridden in DefaultPlayer.model, not here
Entities placed in a map (NPC, monster, etc.)./map/{mapName}.mapThe CostumeManagerComponent block inside that entity's jsonString.@components
Entities that reference a custom model onlyThe 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-search skill — 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*Equip slots only accept a plain Guid. Any prefixed form — including thumbnail://<ruid> — is silently rejected and the slot is left unequipped (no error, no warning). RUIDs returned by msw-search are already plain Guids; do not prepend a scheme. See the msw-sprite-ruid skill 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

PropertyTypeDescription
UseCustomEquipOnlyboolean (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.
DefaultEquipUserIdstringClones 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.
EquippedItemsread-onlyActual 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)MapleAvatarItemCategoryNotes
1CustomBodyEquipBody (1)Skin / body
2CustomHairEquipHair (3)Hair
3CustomFaceEquipFace (4)Face / face shape
4CustomCapEquipCap (5)Hat
5CustomCapeEquipCape (6)Cape
6CustomCoatEquipCoat (7)Coat (top)
7CustomLongcoatEquipLongcoat (9)Longcoat — an item class that occupies both the top and bottom slots
8CustomPantsEquipPants (10)Bottom
9CustomGloveEquipGlove (8)Gloves
10CustomShoesEquipShoes (12)Shoes
11CustomOneHandedWeaponEquipOneHandedWeapon (13)One-handed weapon
12CustomTwoHandedWeaponEquipTwoHandedWeapon (14)Two-handed weapon — occupies both the one-handed weapon slot and the sub-weapon slot
13CustomSubWeaponEquipSubWeapon (15)Sub-weapon
14CustomFaceAccessoryEquipFaceAccessory (16)Face accessory
15CustomEyeAccessoryEquipEyeAccessory (17)Eye accessory
16CustomEarAccessoryEquipEarAccessory (18)Ear accessory
17CustomEarEquipEar (19)Ear (body part)

Enum values without a direct 17-field counterpart

MapleAvatarItemCategoryDescription
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)

  1. Longcoat ↔ Coat + Pants
    Longcoat is designed to occupy both the Coat and Pants slots. When equipping a longcoat, put the longcoat RUID in CustomLongcoatEquip and resolve the combination with coat/pants logically — normally when a longcoat is in use, leave coat/pants empty or avoid conflicting visuals.

  2. 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 on CustomTwoHandedWeaponEquip and make sure values are not also set for one-handed/sub-weapon — avoid double equipping.

  3. Shield ↔ Sub-weapon
    Shield uses the sub-weapon slot. Do not expect another sub-weapon to coexist with CustomSubWeaponEquip.

  4. Empty string = unequip
    Just like SetEquip(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 Values entries already in DefaultPlayer.model. Strings use System.String, mscorlib, ..., booleans use System.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/.

  1. Find the target entity (by name/path/id) in the ContentProto.Entities array.
  2. In jsonString["@components"], find the object with "@type": "MOD.Core.CostumeManagerComponent".
  3. Edit Custom*Equip, UseCustomEquipOnly, DefaultEquipUserId, etc. on that object directly.
  4. Confirm that MOD.Core.CostumeManagerComponent is also listed in the componentNames string 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 categoryCustom*Equip propertyMapleAvatarItemCategory
bodyCustomBodyEquipBody (1)
hairCustomHairEquipHair (3)
faceCustomFaceEquipFace (4)
faceaccessoryCustomFaceAccessoryEquipFaceAccessory (16)
eyeaccessoryCustomEyeAccessoryEquipEyeAccessory (17)
earaccessoryCustomEarAccessoryEquipEarAccessory (18)
capCustomCapEquipCap (5)
capeCustomCapeEquipCape (6)
longcoatCustomLongcoatEquipLongcoat (9)
coatCustomCoatEquipCoat (7)
pantsCustomPantsEquipPants (10)
gloveCustomGloveEquipGlove (8)
shoesCustomShoesEquipShoes (12)
weaponCustomOneHandedWeaponEquipOneHandedWeapon (13)
twohandweaponCustomTwoHandedWeaponEquipTwoHandedWeapon (14)
subweaponCustomSubWeaponEquipSubWeapon (15)
shieldCustomSubWeaponEquipShield (11) — shares the SubWeapon slot

Avatar resource search reference

  • msw-search skill → references/resource/avatar.md: details on GET /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).

MethodSignatureNotes
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:

TermFormatExample
State keyUPPERCASEIDLE, MOVE, ATTACK, HIT, CROUCH, FALL, JUMP, CLIMB, LADDER, DEAD, SIT, ATTACK_WAIT
AvatarBodyActionStateName (Value side)lowercasestand, walk, attack, hit, crouch, fall, rope, ladder, dead, sit, alert, fly, blink, heal
MapleAvatarBodyActionState (enum)PascalCaseStand, Walk, Attack, Hit, Crouch, Fall, Sit, Rope, Ladder, Dead, Blink, Fly, Heal, Alert, Invalid
CoreActionName / PartsActionName (actual sprite action ID)lowercase + digitsstand1, walk1, swingO1, shoot1, prone, jump, alert, etc.

Common confusion: "attack" is not a State. The State is the uppercase ATTACK, the mapping Value is the lowercase attack (= MapleAvatarBodyActionState.Attack), and that Value is then resolved into a sprite action ID such as swingO1 / shoot1 depending on the weapon. From script, the call that triggers the state is StateComponent: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.

PropertyUsed whenTypeNotes
IsLegacySwitch between the two systemsboolean (default false)true = use ActionSheet, false = use StateToAvatarBodyActionSheet
ActionSheetIsLegacy = true (old)SyncDictionary<string, string>State→AnimationKey, e.g. "ATTACK""attack"
StateToAvatarBodyActionSheetIsLegacy = false (new, default)SyncDictionary<string, AvatarBodyActionElement>e.g. "ATTACK" → `{AvatarBodyActionStateName="a

...

用户评价 (0)

发表评价

效果
易用性
文档
兼容性

暂无评价

统计数据

安装量2.7K
评分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 知识卡片
调研问卷