msw-defaultplayer
此技能专注于 MSW DefaultPlayer 角色模型的精细化管理。它允许开发者通过 ModelBuilder 工具,便捷地检查、修补 DefaultPlayer.model 和 Player.model 文件,实现组件的灵活添加与移除。开发者可以轻松配置角色的移动速度、跳跃力、生命值、摄像机行为以及基于地图模式的移动组件。该 Skill 极大地简化了玩家角色的属性和行为定制过程,避免了繁琐的手动文件编辑,从而显著提升了游戏开发效率和角色配置的准确性,确保玩家体验的流畅与一致。
npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-defaultplayerBefore / After 效果对比
1 组在没有此技能之前,开发者需要手动深入复杂的模型文件,逐一查找并修改玩家角色的各项属性(如移动速度、生命值),这不仅耗时,还极易因语法或依赖错误导致游戏崩溃或行为异常,排查问题成本高昂。
此技能通过提供结构化的 ModelBuilder 接口,让开发者能以声明式方式快速配置玩家角色的所有属性和组件。这极大地简化了操作流程,减少了手动错误,显著提升了角色定制的效率和准确性,确保游戏开发顺畅进行。
MSW DefaultPlayer
Use the msw-general ModelBuilder to inspect/patch the DefaultPlayer model file, manage components, and configure movement / physics / HP / camera.
For costume / avatar equipment, see the
msw-avatarskill. Costumes apply not only to DefaultPlayer but to any entity, so they live in a separate skill.
DefaultPlayer overview
What is DefaultPlayer?
The player character model provided by default in the MapleStory Worlds Maker workspace.
- When any user enters a world, a player entity is created based on this model.
- The model ID to use is specified by the
PlayerUriproperty ofDefaultUserEnterLeaveLogic.
File location and structure
DefaultPlayer is made up of two .model files:
| File | Path | Role |
|---|---|---|
| Player.model | ./Global/Player.model | Base model. Defines the Components list and Properties links |
| DefaultPlayer.model | ./Global/DefaultPlayer.model | Inherits Player (BaseModelId: "player"). Overrides Values |
Important: both files are located in
./Global/. Custom script files are created under./RootDesk/MyDesk/.
DefaultPlayer is patched through ModelBuilder
DefaultPlayer is managed through the sibling msw-general/scripts/model/msw_model_builder.cjs, not raw JSON edits.
- Change a property value:
ModelBuilder.read("./Global/DefaultPlayer.model").value(...) - Add/remove a component:
component()/removeComponent() - Check the base component list:
ModelBuilder.snapshot("./Global/Player.model")
File structure detail
Player.model (base)
Path: ./Global/Player.model
EntryKey: model://player
Components: the full list of default components on the player (MOD.Core.* native components)Properties: model property → component property link definitions (properties editable from the inspector)Values: empty (defaults are provided by the engine)
DefaultPlayer.model (override)
Path: ./Global/DefaultPlayer.model
EntryKey: model://defaultplayer
BaseModelId: "player"
Components: only the components added on DefaultPlayer (e.g.script.PlayerHit,script.PlayerAttack)Values: the array of overridden setting values
DefaultPlayer default component list
Native components inherited from Player.model:
| Component | Role |
|---|---|
| TransformComponent | Position, rotation, scale |
| MovementComponent | Movement speed and jump force control |
| RigidbodyComponent | Physics (gravity, footholds), MapleStory-style movement |
| KinematicbodyComponent | Up/down/left/right movement on a RectTileMap |
| SideviewbodyComponent | Side-scrolling movement on a SideViewRectTileMap |
| StateComponent | State machine (Walk, Jump, Dead, etc.) |
| AvatarRendererComponent | Avatar rendering, color, emotion |
| AvatarStateAnimationComponent | State → avatar animation mapping |
| CostumeManagerComponent | Equipment / costume management → see the msw-avatar skill for details |
| CameraComponent | Camera tracking settings |
| PlayerControllerComponent | Input-to-action mapping, condition handling |
| PlayerComponent | HP, death/revive, PVP, map travel |
| ChatBalloonComponent | Chat balloon |
| NameTagComponent | Name tag |
| DamageSkinSettingComponent | Damage skin |
| DamageSkinSpawnerComponent | Damage skin spawn |
| HitEffectSpawnerComponent | Hit effect spawn |
| TriggerComponent | Collision detection |
| InventoryComponent | Inventory |
Script components added in DefaultPlayer.model:
| Component | Role |
|---|---|
| script.PlayerHit | Player hit-handling logic |
| script.PlayerAttack | Player attack logic |
Quick reference — key components
| Component | Role | Key properties / methods |
|---|---|---|
| PlayerComponent | HP, death/revive, PVP, map travel | Hp, MaxHp, PVPMode, RespawnDuration, RespawnPosition, UserId, IsDead(), ProcessDead(), ProcessRevive(), MoveToMapPosition() |
| PlayerControllerComponent | Input → action mapping, conditional control | SetActionKey(key, actionName), ActionAttack(), ActionJump(), LookDirectionX |
| MovementComponent | High-level movement speed / jump interface | InputSpeed (default 1.0), JumpForce (default 1), Jump(), Stop() |
| RigidbodyComponent | Maple side-view physics (gravity / footholds) | Gravity, WalkAcceleration, WalkSpeed, AddForce(), IsOnGround() |
| KinematicbodyComponent | Top-view up/down/left/right movement on RectTile | (RectTile map-mode only) |
| SideviewbodyComponent | Side-view on SideViewRectTile | (SideViewRectTile map-mode only) |
| AvatarRendererComponent | Avatar rendering / color / emotion | SetColor(), SetAlpha(), PlayEmotion(), PlayRate |
| StateComponent | State machine (Walk/Jump/Dead) | CurrentStateName, ChangeState(), DeadEvent/ReviveEvent |
| NameTagComponent | Name tag | Name, FontSize, FontColor, NameTagRUID |
| ChatBalloonComponent | Chat balloon | Message, ChatModeEnabled, ShowDuration |
| CameraComponent | Camera tracking | DeadZone, SoftZone, Damping, ScreenOffset |
| TriggerComponent | Collision detection | BoxSize, Offset, CollisionGroup |
DefaultPlayer Values structure
Format of each entry in the Values array of DefaultPlayer.model:
{
"TargetType": "<component name> or null",
"Name": "<property name>",
"ValueType": {
"$type": "MODNativeType",
"type": "<type info>"
},
"Value": <value>
}
TargetType rules
null: a model property defined in Player.model's Properties (linked through Properties to the actual component property)"MOD.Core.<ComponentName>": directly override a property on a specific native component"script.<ScriptName>": a property of a custom script component
Model properties (TargetType: null)
Mapped to actual component properties through the links defined in Player.model's Properties.
| Model property name | Source component.property | Description | Default |
|---|---|---|---|
| speed | MovementComponent.InputSpeed | Movement speed | 1.0 |
| jumpForce | MovementComponent.JumpForce | Jump height | 1.0 |
| walkAcceleration | RigidbodyComponent.WalkAcceleration | Acceleration / deceleration | 1.0 |
| gravity | RigidbodyComponent.Gravity | Gravity | 1.0 |
| cameraDeadZone | CameraComponent.DeadZone | Camera dead zone | {x: 0.052, y: 0.08} |
| cameraSoftZone | CameraComponent.SoftZone | Camera soft zone | {x: 0.268, y: 0.7} |
| cameraDamping | CameraComponent.Damping | Camera smooth-follow | {x: 2.5, y: 3.9} |
| cameraScreen | CameraComponent.ScreenOffset | Dead-zone center point | {x: 0.5, y: 0.655} |
| cameraDutch | CameraComponent.DutchAngle | Camera rotation | 0.0 |
| cameraOffset | CameraComponent.CameraOffset | Camera position offset | {x: 0.0, y: 0.0} |
| message | ChatBalloonComponent.Message | Chat balloon message | "" |
| chatModeEnabled | ChatBalloonComponent.ChatModeEnabled | Whether chat is processed (e.g. balloon shown) | true |
| nameTag | NameTagComponent.Name | Name tag | "" |
| damageSkinId | DamageSkinSettingComponent.DamageSkinId | Damage skin type | DataRef |
| damageDelayPerAttack | DamageSkinSettingComponent.DelayPerAttack | Damage delay | 0.05 |
| triggerBodyBoxSize | TriggerComponent.BoxSize | Collision detection area size | {x: 0.66, y: 0.7} |
| triggerBodyBoxOffset | TriggerComponent.BoxOffset | Collision detection area offset | {x: 0.0, y: 0.35} |
| triggerBodyColliderOffset | TriggerComponent.ColliderOffset | Collider offset | {x: 0.0, y: 0.35} |
| maxHp | PlayerComponent.MaxHp | Max HP | 1000 |
Direct component override (TargetType: specific component)
Values that directly override a component property rather than going through a model-property link:
| TargetType | Name | Description | Default |
|---|---|---|---|
| MOD.Core.CameraComponent | ZoomRatioMax | Camera max zoom ratio | 500.0 |
| MOD.Core.MovementComponent | JumpForce | Jump force (direct override) | 1.0 |
| MOD.Core.MovementComponent | InputSpeed | Movement speed (direct override) | 1.0 |
| script.PlayerHit | CollisionGroup | Hit collision group | CollisionGroup ID |
| script.PlayerHit | BoxSize | Hit collision area size | {x: 0.45, y: 0.7} |
| script.PlayerHit | ColliderOffset | Hit collision offset | {x: 0.0, y: 0.35} |
Movement components per map mode
See
msw-general/references/platform.md§4 for the TileMapMode↔Body mapping table. Depending on the map mode, one of RigidbodyComponent / KinematicbodyComponent / SideviewbodyComponent is active.
Identifying a player (for script reference)
entity.PlayerComponent ~= nil→ whether the entity is a player_UserService.LocalPlayer→ my player entity (client-only)_UserService:GetUserEntityByUserId(userId)→ player entity for a specific user
Player entity runtime structure (root vs children)
A spawned player is not a single flat entity. At runtime the engine builds a small hierarchy under the player root, and avatar action selectors live on grandchild entities — not on the root. This affects how you look up components and how you trigger avatar poses.
Component → entity mapping
| Component | Where it lives | Lookup |
|---|---|---|
PlayerComponent | Root | player:GetComponent("PlayerComponent") (or player.PlayerComponent) |
StateComponent | Root | player:GetComponent("StateComponent") |
MovementComponent | Root | player:GetComponent("MovementComponent") |
AvatarRendererComponent | Root | player.AvatarRendererComponent |
AvatarStateAnimationComponent | Root | player:GetComponent("AvatarStateAnimationComponent") |
PlayerControllerComponent | Root | player.PlayerControllerComponent |
AvatarBodyActionSelectorComponent | Body grandchild of root (under the avatar root) | not reachable via player:GetComponent(...) — see below |
AvatarFaceActionSelectorComponent | Face grandchild of root | not reachable via player:GetComponent(...) — see below |
player:GetComponent("AvatarBodyActionSelectorComponent") returns nil and the LSP does not warn (the signature is Component, not nilable). The failure is only visible at runtime.
How to reach the selectors
-- ClientOnly context (recommended — the direct API)
local body = self.Entity.AvatarRendererComponent:GetBodyEntity()
local face = self.Entity.AvatarRendererComponent:GetFaceEntity()
local bodySelector = body and body:GetComponent("AvatarBodyActionSelectorComponent")
local faceSelector = face and face:GetComponent("AvatarFaceActionSelectorComponent")
-- Server or "either side" context (GetBodyEntity / GetFaceEntity are ClientOnly)
local bodySelector = self.Entity:GetFirstChildComponentByTypeName(
"AvatarBodyActionSelectorComponent", true)
AvatarRendererComponent:GetBodyEntity()andGetFaceEntity()are declared@ExecSpace("ClientOnly")— calling them from a server-side method returnsnil. UseGetFirstChildComponentByTypeName(name, recursive=true)as the cross-side fallback.
Triggering avatar poses — use StateComponent, never write the selector directly
A live player has a state machine running every tick: PlayerControllerComponent evaluates input/movement → StateComponent transitions (IDLE / MOVE / etc.) → AvatarStateAnimationComponent.ReceiveStateChangeEvent translates that into BodyActionStateChangeEvent → the selector's ActionState is rewritten.
This means writing AvatarBodyActionSelectorComponent.ActionState directly is a silent overwrite: the value applies for one frame, then the next state-machine tick maps the current StateComponent state back onto the selector and your write is gone. Logs print ActionState=Attack immediately after the assignment, but in play mode the pose flickers for one frame and disappears.
Correct entry point
local state = self.Entity:GetComponent("StateComponent")
state:ChangeState("ATTACK") -- ActionSheet keys are UPPERCASE
State keys come from the player's ActionSheet. The DefaultPlayer ships with 11 UPPERCASE keys, each mapped to a default animation:
StateComponent:ChangeState(...) key | Default animation |
|---|---|
"IDLE" | stand |
"MOVE" | walk |
"ATTACK" | attack |
"HIT" | hit |
"CROUCH" | crouch |
"FALL" | fall |
"JUMP" | fall |
"CLIMB" | rope |
"LADDER" | ladder |
"DEAD" | dead |
"SIT" | sit |
The state machine auto-returns to IDLE once the action finishes — no manual restore timer needed.
Casing pitfall: the string key passed to
ChangeStateis UPPERCASE ("ATTACK"). The enum value written toselector.ActionStatedirectly (NPC path — see below) isMapleAvatarBodyActionState.Attack— PascalCase, separate API surface, same underlying state. Wrong casing on the string side ("attack"/"Attack") misses the ActionSheet mapping silently — no warning.
When can you write the selector directly?
Only on entities without a running PlayerControllerComponent + StateComponent + AvatarStateAnimationComponent stack — e.g. NPCs / monsters that use the avatar renderer for visuals but have no input controller driving a state machine. On those, selector.ActionState = MapleAvatarBodyActionState.<Pose> sticks. On DefaultPlayer-shaped entities, route through StateComponent:ChangeState(...) instead.
Key services at a glance (for script reference)
| Service | Role | Key API |
|---|---|---|
| _UserService | User management, enter/leave | LocalPlayer, UserEntities, GetUserEntityByUserId(), UserEnterEvent/UserLeaveEvent |
| _TeleportService | Teleport / map travel | TeleportToEntity(), TeleportToMapPosition(), WarpUserToWorldAsync() |
| _CameraService | Camera control | SwitchCameraTo(), ZoomTo(), ZoomReset() |
| DefaultUserEnterLeaveLogic | User enter/leave logic | PlayerUri (player model ID), StartPoint (starting map) |
How to modify DefaultPlayer
Changing property values (Values)
Load ./Global/DefaultPlayer.model with ModelBuilder.read(), then update values with value().
Example: set movement speed to 2.0
const { ModelBuilder } = require("../msw-general/scripts/model/msw_model_builder.cjs");
const b = ModelBuilder.read("./Global/DefaultPlayer.model");
b.value(null, "speed", 2.0, "float")
.value("MovementComponent", "InputSpeed", 2.0, "float")
.write("./Global/DefaultPlayer.model");
Note: both the model property (
TargetType: null,Name: "speed") and the direct component override (`TargetType: "MOD.Core.MovementCompon
...
用户评价 (0)
发表评价
暂无评价
统计数据
用户评分
为此 Skill 评分