mcpField
A
advanced-input

advanced-input

github.com/dcl-regenesislabs开发1738 次安装21 周前更新
代码仓库

关于 advanced-input

面向 Decentraland 场景开发者的高级输入处理技能,介绍指针状态、输入轮询与移动限制,适用于构建复杂交互。

主要功能

  1. 01检测指针锁定状态与变化
  2. 02读取光标坐标和世界射线
  3. 03按实体或全局轮询输入
  4. 04按标签批量处理实体输入
  5. 05限制玩家移动并冻结角色
  6. 06轮询按键控制自定义移动

适用场景

  1. 01实现按键触发和持续输入
  2. 02批量处理同类实体交互
  3. 03制作过场动画中的角色冻结
  4. 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_PRIMARYE 键
IA_SECONDARYF 键
IA_ACTION_31 键
IA_ACTION_42 键
IA_ACTION_53 键
IA_ACTION_64 键
IA_JUMP空格键
IA_FORWARDW 键
IA_BACKWARDS 键
IA_LEFTA 键
IA_RIGHTD 键
IA_WALKShift 键

事件类型

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