关于 advanced-input
面向 Decentraland 场景开发者的高级输入处理技能,介绍指针状态、输入轮询与移动限制,适用于构建复杂交互。
主要功能
- 01检测指针锁定状态与变化
- 02读取光标坐标和世界射线
- 03按实体或全局轮询输入
- 04按标签批量处理实体输入
- 05限制玩家移动并冻结角色
- 06轮询按键控制自定义移动
适用场景
- 01实现按键触发和持续输入
- 02批量处理同类实体交互
- 03制作过场动画中的角色冻结
- 04用 WASD 控制自定义实体
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dcl-regenesislabs/ opendcl --skill advanced-input安装后在 Claude Code 中输入/advanced-input确认已加载。
详细介绍
Decentraland 中的高级输入处理
有关基本点击/悬停事件,请参阅 add-interactivity 技能。本技能介绍高级输入模式。
指针锁定状态
检测光标是被捕获(第一人称模式)还是可自由移动:
import { engine, PointerLock } from '@dcl/sdk/ecs'
function checkPointerLock() {
const isLocked = PointerLock.get(engine.CameraEntity).isPointerLocked
if (isLocked) {
// Cursor is captured — player is in first-person control
} else {
// Cursor is free — player can click UI elements
}
}
engine.addSystem(checkPointerLock)
检测指针锁定状态变化
PointerLock.onChange(engine.CameraEntity, (pointerLock) => {
if (pointerLock?.isPointerLocked) {
console.log('Cursor locked')
} else {
console.log('Cursor unlocked')
}
})
光标位置和世界射线
获取光标的屏幕位置,以及它投射到 3D 世界中的射线:
import { engine, PrimaryPointerInfo } from '@dcl/sdk/ecs'
function readPointer() {
const pointerInfo = PrimaryPointerInfo.get(engine.RootEntity)
console.log('Cursor position:', pointerInfo.screenCoordinates)
console.log('Cursor delta:', pointerInfo.screenDelta)
console.log('World ray direction:', pointerInfo.worldRayDirection)
}
engine.addSystem(readPointer)
使用 inputSystem 轮询输入
按实体检查输入命令
检查特定实体上是否发生了特定输入操作:
import { engine, inputSystem, InputAction, PointerEventType } from '@dcl/sdk/ecs'
function myInputSystem() {
// Check for click on a specific entity
const clickData = inputSystem.getInputCommand(
InputAction.IA_POINTER,
PointerEventType.PET_DOWN,
myEntity
)
if (clickData) {
console.log('Entity clicked via system:', clickData.hit.entityId)
}
}
engine.addSystem(myInputSystem)
全局输入检查
无论玩家的光标是否位于某个实体上,都会触发。
function globalInputSystem() {
// Was the key just pressed this frame?
if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN)) {
console.log('E key pressed!')
}
// Is the key currently held down?
if (inputSystem.isPressed(InputAction.IA_SECONDARY)) {
console.log('F key is held!')
}
}
engine.addSystem(globalInputSystem)
基于标签批量处理输入
如果有许多相似实体都响应相同的输入(例如,每个木桶都响应 E 键并被打破),请通过 Tag 组件为它们添加标签,并在每一帧遍历标签查询:
import { engine, Tag, inputSystem, InputAction, PointerEventType } from '@dcl/sdk/ecs'
// At setup: tag the entities (or declare Tag in main-entities.ts).
for (const barrel of [b1, b2, b3]) {
Tag.createOrReplace(barrel, { value: 'breakable' })
}
engine.addSystem(() => {
for (const [entity] of engine.getEntitiesByTag('breakable')) {
const cmd = inputSystem.getInputCommand(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN, entity)
if (cmd) {
// entity was the IA_PRIMARY target this frame
engine.removeEntity(entity)
}
}
})
当行为一致时,这比为 N 个实体分别注册 pointerEventsSystem.onPointerDown 处理程序更加简洁。
所有 InputAction 值
| InputAction | 按键/按钮 |
|---|---|
IA_POINTER | 鼠标左键 |
IA_PRIMARY | E 键 |
IA_SECONDARY | F 键 |
IA_ACTION_3 | 1 键 |
IA_ACTION_4 | 2 键 |
IA_ACTION_5 | 3 键 |
IA_ACTION_6 | 4 键 |
IA_JUMP | 空格键 |
IA_FORWARD | W 键 |
IA_BACKWARD | S 键 |
IA_LEFT | A 键 |
IA_RIGHT | D 键 |
IA_WALK | Shift 键 |
事件类型
PointerEventType.PET_DOWN // Button/key pressed
PointerEventType.PET_UP // Button/key released
PointerEventType.PET_HOVER_ENTER // Cursor enters entity
PointerEventType.PET_HOVER_LEAVE // Cursor leaves entity
InputModifier(限制移动)
限制或冻结玩家的移动:
import { engine, InputModifier } from '@dcl/sdk/ecs'
// Freeze player completely
InputModifier.create(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({ disableAll: true })
})
// Restrict specific movement (every flag is optional and defaults to false)
InputModifier.createOrReplace(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({
disableWalk: false,
disableJog: false,
disableRun: true,
disableJump: true,
disableDoubleJump: true,
disableGliding: true,
disableEmote: true
})
})
// Restore normal movement
InputModifier.deleteFrom(engine.PlayerEntity)
受限期间:重力仍然生效,摄像机仍可旋转,指针/接近事件仍会触发。玩家离开场景时,所有限制都会自动解除。
重要提示: InputModifier 仅适用于 DCL 2.0 桌面客户端。在网页浏览器探索器中无效。
过场动画模式
在电影序列期间冻结玩家:
function startCutscene() {
// Freeze player
InputModifier.create(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({ disableAll: true })
})
// ... play cinematic with VirtualCamera ...
// After cutscene ends, restore movement
// InputModifier.deleteFrom(engine.PlayerEntity)
}
WASD 移动模式
轮询移动按键以控制自定义实体:
import { engine, inputSystem, InputAction, PointerEventType, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const MOVE_SPEED = 5
function customMovementSystem(dt: number) {
const transform = Transform.getMutable(controllableEntity)
let moveX = 0
let moveZ = 0
if (inputSystem.isPressed(InputAction.IA_FORWARD)) moveZ += 1
if (inputSystem.isPressed(InputAction.IA_BACKWARD)) moveZ -= 1
if (inputSystem.isPressed(InputAction.IA_LEFT)) moveX -= 1
if (inputSystem.isPressed(InputAction.IA_RIGHT)) moveX += 1
transform.position.x += moveX * MOVE_SPEED * dt
transform.position.z += moveZ * MOVE_SPEED * dt
}
engine.addSystem(customMovementSystem)
组合输入模式
使用数字键的操作栏
function actionBarSystem() {
if (inputSystem.isTriggered(InputAction.IA_ACTION_3, PointerEventType.PET_DOWN)) {
console.log('Slot 1 activated')
useAbility(1)
}
if (inputSystem.isTriggered(InputAction.IA_ACTION_4, PointerEventType.PET_DOWN)) {
console.log('Slot 2 activated')
useAbility(2)
}
if (inputSystem.isTriggered(InputAction.IA_ACTION_5, PointerEventType.PET_DOWN)) {
console.log('Slot 3 activated')
useAbility(3)
}
if (inputSystem.isTriggered(InputAction.IA_ACTION_6, PointerEventType.PET_DOWN)) {
console.log('Slot 4 activated')
useAbility(4)
}
}
engine.addSystem(actionBarSystem)
最佳实践
- 对单次触发的操作(开火、开门)使用
isTriggered()——它仅在按键首次按下的那一帧返回 true - 对持续操作(移动、举盾)使用
isPressed()——按住按键时,它会在每一帧返回 true getInputCommand()会提供命中数据(位置、实体)——需要知道点击了什么时使用它- 简单的实体点击优先使用
pointerEventsSystem.onPointerDown()——复杂的多键或轮询模式则使用inputSystem - InputModifier 仅适用于 DCL 2.0 桌面客户端——如果场景依赖它,请使用桌面客户端进行测试
- WASD 按键(
IA_FORWARD等)也会控制玩家移动——轮询这些按键可以读取移动状态,但不会覆盖该状态
有关基本指针事件和点击处理程序,请参阅 add-interactivity 技能。
评论
支持 Markdown