player-physics
Player Physics in Decentraland
Apply forces to the player's avatar using the Physics API. All physics operations affect the local player only.
import { Physics } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
Impulse (One-Shot Push)
Apply a single instantaneous force — useful for launch pads, jumps, and explosions.
// Launch straight up
Physics.applyImpulseToPlayer(Vector3.create(0, 50, 0))
// Pass direction + magnitude separately (direction is normalized automatically)
Physics.applyImpulseToPlayer(Vector3.create(0, 1, 0), 50)
Multiple applyImpulseToPlayer() calls within the same frame are accumulated and applied as a single combined impulse.
Launch Pad Example
import { Physics, TriggerArea, triggerAreaEventsSystem, ColliderLayer, MeshRenderer, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const launchPad = engine.addEntity()
Transform.create(launchPad, { position: Vector3.create(8, 0, 8) })
MeshRenderer.setBox(launchPad)
TriggerArea.setBox(launchPad, ColliderLayer.CL_PLAYER)
triggerAreaEventsSystem.onTriggerEnter(launchPad, (result) => {
if (result.trigger?.entity !== engine.PlayerEntity) return
Physics.applyImpulseToPlayer(Vector3.create(0, 50, 0))
})
Knockback (Push Away from a Point)
Push the player away from a source position with one impulse. The direction is computed automatically from source to player. Use for explosions, impacts, and area-of-effect blasts.
import { Physics, KnockbackFalloff } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
// Basic knockback from explosion center
Physics.applyKnockbackToPlayer(Vector3.create(8, 1, 8), 40)
// With radius and falloff
Physics.applyKnockbackToPlayer(
Vector3.create(8, 1, 8),
40, // magnitude
10, // radius: no effect beyond 10 meters
KnockbackFalloff.LINEAR // magnitude fades linearly to 0 at edge
)
KnockbackFalloff Options
| Falloff | Behavior |
|---|---|
KnockbackFalloff.CONSTANT |
Same magnitude at any distance within radius (default) |
KnockbackFalloff.LINEAR |
Smooth linear decrease to 0 at the radius edge |
KnockbackFalloff.INVERSE_SQUARE |
Sharp, physically-realistic drop-off |
Notes:
- If the player is exactly at the source position, they are pushed straight up.
- A negative magnitude pulls the player toward the point instead of pushing them away.
- The same falloff values apply to
applyRepulsionForceToPlayer().
Continuous Force
Apply a persistent directional force that stays active until explicitly removed. Use for wind tunnels, conveyor belts, and gravity fields.
Each continuous force is identified by an entity — that entity acts as the "owner" of the force. Multiple forces from different entities stack.
// Apply continuous sideways push
Physics.applyForceToPlayer(windZoneEntity, Vector3.create(10, 0, 0))
// With separate direction + magnitude
Physics.applyForceToPlayer(windZoneEntity, Vector3.create(0, 1, 0), 50)
// Remove the force
Physics.removeForceFromPlayer(windZoneEntity)
Wind Tunnel Example
import { Physics, TriggerArea, triggerAreaEventsSystem, ColliderLayer, MeshRenderer, MeshCollider, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const windTunnel = engine.addEntity()
Transform.create(windTunnel, {
position: Vector3.create(8, 1, 8),
scale: Vector3.create(4, 3, 4),
})
MeshRenderer.setBox(windTunnel)
TriggerArea.setBox(windTunnel, ColliderLayer.CL_PLAYER)
triggerAreaEventsSystem.onTriggerEnter(windTunnel, (result) => {
if (result.trigger?.entity !== engine.PlayerEntity) return
Physics.applyForceToPlayer(windTunnel, Vector3.create(15, 0, 0))
})
triggerAreaEventsSystem.onTriggerExit(windTunnel, (result) => {
if (result.trigger?.entity !== engine.PlayerEntity) return
Physics.removeForceFromPlayer(windTunnel)
})
Timed Force
Apply a force for a fixed duration, then it expires automatically.
import { Physics, timers } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const gustEntity = engine.addEntity()
// Strong upward force for 1.5 seconds
Physics.applyForceToPlayerForDuration(gustEntity, 1.5, Vector3.create(0, 50, 0))
// With separate direction + magnitude
Physics.applyForceToPlayerForDuration(gustEntity, 1.5, Vector3.create(0, 1, 0), 50)
Repulsion Force
Push the player away from a fixed point continuously, with distance-based falloff. The force direction is recalculated each frame as the player moves.
import { Physics, timers } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const repulsionSource = engine.addEntity()
Transform.create(repulsionSource, { position: Vector3.create(8, 1, 8) })
// Pass the entity's position explicitly as the repulsion origin
Physics.applyRepulsionForceToPlayer(
repulsionSource,
Transform.get(repulsionSource).position, // origin — passed explicitly, not read automatically
50, // magnitude
10, // radius of effect in meters
)
// Remove after 500ms
timers.setTimeout(() => {
Physics.removeForceFromPlayer(repulsionSource)
}, 500)
Note: The position must be passed explicitly — the API does not read it from the entity's Transform automatically.
Coordinate Conversion (Local to World Direction)
When you want to push the player relative to an entity's local orientation (e.g., "forward from this cannon"), convert the local direction to world space first:
import { Physics, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
// Push the player in the direction the entity's local +Z axis points in world space
const worldDir = Transform.localToWorldDirection(myEntity, Vector3.create(0, 0, 1))
Physics.applyImpulseToPlayer(worldDir, 20)
Quick Reference
| Method | Type | Description |
|---|---|---|
Physics.applyImpulseToPlayer(dir, mag?) |
One-shot | Instant directional push |
Physics.applyKnockbackToPlayer(pos, mag, radius?, falloff?) |
One-shot | Push away from point |
Physics.applyForceToPlayer(entity, dir, mag?) |
Continuous | Persistent push while active |
Physics.removeForceFromPlayer(entity) |
Control | Stop a continuous force |
Physics.applyForceToPlayerForDuration(entity, secs, dir, mag?) |
Timed | Force that expires automatically |
Physics.applyRepulsionForceToPlayer(entity, pos, mag, radius) |
Continuous | Distance-based push from point |
Transform.localToWorldDirection(entity, dir) |
Utility | Convert local direction to world space |
Best Practices
- Use
applyImpulseToPlayerfor one-off events (jump pads, explosions, hits) - Use
applyForceToPlayer+removeForceFromPlayerwith trigger zones for areas (wind tunnels, conveyor belts) - Use
KnockbackFalloff.LINEARfor most area effects — it feels natural and predictable - Always check
result.trigger?.entity !== engine.PlayerEntityin trigger callbacks to only affect the local player - A negative knockback magnitude creates a pull/gravity well effect
- Multiple forces from different entities stack — each entity tracks its own force independently
More from decentraland/sdk-skills
decentraland-sdk-skills
Build, extend, and deploy Decentraland SDK7 scenes. Contains agent behavioral guidelines, the composite-first rule, and an index of all topic skills with reference files. Install with a single command — no flags needed.
22build-ui
Build 2D screen-space UI for Decentraland scenes using React-ECS (JSX). Create HUDs, menus, health bars, scoreboards, dialogs, buttons, inputs, and dropdowns. Use when the user wants screen overlays, on-screen UI, HUD elements, menus, or form inputs. Do NOT use for 3D in-world text (see advanced-rendering) or clickable 3D objects (see add-interactivity).
3advanced-input
System-level input polling and player movement control in Decentraland. Covers inputSystem (isTriggered/isPressed for held keys, WASD polling), InputModifier (freeze/restrict player movement), PointerLock (cursor capture detection), PrimaryPointerInfo (cursor screen coords and world ray), and number-key action bar patterns. Use when the user wants continuous key polling, WASD-controlled entities, to freeze the player during a cutscene, FPS-style cursor lock, or multi-key combo patterns. For event-driven clicks and hover on entities see add-interactivity.
3nft-blockchain
NFT display and blockchain interaction in Decentraland. NftShape (framed NFT artwork), wallet checks (getPlayer, isGuest), signedFetch (authenticated requests), smart contract interaction (eth-connect, createEthereumProvider), and RPC calls. Use when the user wants NFTs, blockchain, wallet, smart contracts, Web3, crypto, or token gating. Do NOT use for player avatar data or emotes (see player-avatar).
3camera-control
Control camera behavior in Decentraland scenes. CameraMode detection (first/third person, onChange listener), CameraModeArea (force a mode inside a box), VirtualCamera (cinematic scripted cameras with Speed/Time transitions and lookAtEntity), MainCamera (activate/deactivate virtual cameras), and camera vs collider interactions (CL_PHYSICS + CL_POINTER). Use when the user wants camera control, cutscenes, cinematic views, forced camera modes, or camera tracking. Do NOT use for input restriction during cutscenes (see advanced-input for InputModifier) or cursor lock detection (see advanced-input for PointerLock).
3scene-runtime
Cross-cutting runtime APIs for Decentraland SDK7 scenes. Covers async work (executeTask), HTTP (fetch, signedFetch, getHeaders), WebSocket, timers (timers.setTimeout/setInterval — native setTimeout is unavailable), realm/scene info (getRealm, getSceneInformation, getExplorerInformation), world time (getWorldTime), reading deployed files (readFile), EngineInfo frame timing, Component.onChange listeners, removeEntityWithChildren, restricted actions (movePlayerTo, teleportTo, triggerEmote, openExternalUrl, openNftDialog, copyToClipboard, changeRealm, triggerSceneEmote), and the @dcl/sdk/testing framework (test, assertEquals, assertComponentValue, assertEntitiesCount). Use when the user needs async, HTTP, WebSocket, timers, realm/scene metadata, restricted actions, or to write scene tests. Do NOT use for UI (see build-ui), multiplayer sync (see multiplayer-sync), avatar/player data (see player-avatar), or polling-based input (see advanced-input).
3