如何在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
相关产品推荐
相关产品推荐

