You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Three Fiber中实现人物角色的移动控制?

React Three Fiber 键盘控制3D模型移动实现方案

R3F生态下实现键盘控制有两种成熟方案,可根据项目复杂度选择:

方案1:使用官方生态封装Hook(优先推荐)

@react-three/drei 提供的useKeyboard已经内置了键盘事件绑定、状态同步、内存泄漏处理逻辑,不需要手动处理事件监听,适合绝大多数常规场景。
示例代码如下:

import { useFrame } from '@react-three/fiber'
import { useKeyboard } from '@react-three/drei'
import { useRef } from 'react'
import * as THREE from 'three'

function ControlledModel() {
  const modelRef = useRef(null)
  // 移动、旋转速度可根据需求自行调整
  const MOVE_SPEED = 0.1
  const ROTATE_SPEED = 0.05

  // 获取按键实时状态,按下对应键返回true,松开返回false
  const keys = useKeyboard()

  useFrame(() => {
    if (!modelRef.current) return

    // 模型自身坐标系前后移动(W/S键)
    if (keys.KeyW) modelRef.current.translateZ(-MOVE_SPEED)
    if (keys.KeyS) modelRef.current.translateZ(MOVE_SPEED)

    // 模型自身坐标系左右平移(A/D键)
    if (keys.KeyA) modelRef.current.translateX(-MOVE_SPEED)
    if (keys.KeyD) modelRef.current.translateX(MOVE_SPEED)

    // 模型左右旋转(Q/E键,可选功能)
    if (keys.KeyQ) modelRef.current.rotation.y += ROTATE_SPEED
    if (keys.KeyE) modelRef.current.rotation.y -= ROTATE_SPEED
  })

  return (
    <mesh ref={modelRef}>
      {/* 此处可替换为你自己加载的3D模型,比如useGLTF导入的模型节点 */}
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color="orange" />
    </mesh>
  )
}

注意事项:

  • useKeyboard返回对象的key对应键盘事件的code属性,比如空格对应Space、左Shift对应ShiftLeft,可直接打印keys对象查看所有按键的状态映射
  • 如果需要基于世界坐标系而非模型自身坐标系移动,直接修改modelRef.current.position.x/position.z属性即可,不需要调用translate相关方法

方案2:自定义事件监听(适合高度定制化场景)

如果项目不需要引入drei,或者需要自定义按键映射、长按/连击判定、组合键等特殊逻辑,可以手动封装事件监听,注意要在组件卸载时解绑事件避免内存泄漏。
示例代码如下:

import { useFrame } from '@react-three/fiber'
import { useRef, useEffect, useState } from 'react'

function ControlledModel() {
  const modelRef = useRef(null)
  const MOVE_SPEED = 0.1
  // 存储按键状态
  const [activeKeys, setActiveKeys] = useState({
    w: false,
    a: false,
    s: false,
    d: false
  })

  useEffect(() => {
    const handleKeyDown = (e) => {
      setActiveKeys(prev => ({ ...prev, [e.key.toLowerCase()]: true }))
    }
    const handleKeyUp = (e) => {
      setActiveKeys(prev => ({ ...prev, [e.key.toLowerCase()]: false }))
    }

    window.addEventListener('keydown', handleKeyDown)
    window.addEventListener('keyup', handleKeyUp)
    // 组件卸载时解绑事件
    return () => {
      window.removeEventListener('keydown', handleKeyDown)
      window.removeEventListener('keyup', handleKeyUp)
    }
  }, [])

  useFrame(() => {
    if (!modelRef.current) return
    // 世界坐标系移动逻辑
    if (activeKeys.w) modelRef.current.position.z -= MOVE_SPEED
    if (activeKeys.s) modelRef.current.position.z += MOVE_SPEED
    if (activeKeys.a) modelRef.current.position.x -= MOVE_SPEED
    if (activeKeys.d) modelRef.current.position.x += MOVE_SPEED
  })

  return (
    <mesh ref={modelRef}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color="blue" />
    </mesh>
  )
}

额外功能实现&性能优化建议

  • 第三人称跟随视角:在useFrame回调中添加相机同步逻辑即可,示例:
    // 写在模型位置更新之后
    camera.position.x = modelRef.current.position.x
    camera.position.z = modelRef.current.position.z + 5 // 5为跟随距离,可自行调整
    camera.position.y = modelRef.current.position.y + 2 // 2为视角高度,可自行调整
    camera.lookAt(modelRef.current.position)
    
  • 不要在React state中存储高频更新的移动参数,所有模型属性修改直接通过ref操作,避免触发不必要的React重渲染
  • 涉及碰撞检测的场景,将移动逻辑和碰撞判定写在同一个useFrame回调中,避免状态不同步导致的穿模问题

内容的提问来源于stack exchange,提问作者raghav-wd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 02:54:05