msw-defaultplayer
This skill specializes in the detailed management of the MSW DefaultPlayer character model. It empowers developers to easily inspect and patch DefaultPlayer.model and Player.model files using the ModelBuilder tool, enabling flexible addition and removal of components. Developers can effortlessly configure character movement speed, jump force, HP, camera behavior, and map-mode-specific movement components. This Skill significantly simplifies the customization of player character attributes and behaviors, bypassing tedious manual file editing. Consequently, it boosts game development efficiency and the precision of character configurations, ensuring a smooth and consistent player experience.
npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-defaultplayerBefore / After Comparison
1 组Before this skill, developers had to manually delve into complex model files to locate and modify various player character attributes (e.g., movement speed, HP). This was not only time-consuming but also highly prone to syntax or dependency errors, leading to game crashes or abnormal behavior, with high debugging costs.
This skill provides a structured ModelBuilder interface, allowing developers to quickly configure all player character attributes and components declaratively. This greatly simplifies the operational process, reduces manual errors, and significantly boosts the efficiency and accuracy of character customization, ensuring smooth game development.
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
...
User Reviews (0)
Write a Review
No reviews yet
Statistics
User Rating
Rate this Skill