three-player-controller
v0.6.2
Published
Third-person / first-person player controller for three.js (three-mesh-bvh based)
Readme
中文 | English
three-player-controller
基于 three.js 的轻量级第一 / 第三人称玩家控制器。提供角色胶囊体碰撞、动画、相机避障、移动平台、动态刚体、车辆控制和 Foot IK,并使用 three-mesh-bvh 加速复杂场景中的碰撞查询。
功能
- 第一 / 第三人称切换
- 行走、奔跑、跳跃、飞行
- 角色胶囊体碰撞与地面检测
- 相机避障、过肩视角、弹簧相机
- 静态 / 运动学 / 动态碰撞体
- 动态球体、盒体及刚体碰撞
- BVH Worker 异步构建
- 3D Tiles 流式碰撞体构建
- 内置车辆物理、悬挂、转向、抓地和手刹
- Foot IK 插件
- 自定义动画与 Locomotion Set
- 移动端虚拟摇杆与按钮
- 自定义键位与外部输入
示例
安装
npm install three-player-controller three-mesh-bvh本地运行
git clone https://github.com/hh-hang/three-player-controller.git
cd three-player-controller
npm install
npm run dev浏览器访问 http://localhost:5173/three-player-controller/。
快速开始
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { playerController } from "three-player-controller";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
1000,
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
// 加载人物模型
const loader = new GLTFLoader();
const playerGltf = await loader.loadAsync("./glb/player.glb");
// 创建玩家控制器
const player = new playerController();
// 人物控制初始化
await player.init({
scene, // three.js 场景实例
camera, // three.js 相机实例
controls, // 外部相机控制器
// 人物模型、动画与移动参数
playerModelConfig: {
model: playerGltf.scene, // 外部加载后的模型根节点
animations: playerGltf.animations, // 动画片段
scale: 0.001, // 模型缩放
idleAnim: "idle", // 静止动画名
walkAnim: "walk", // 行走动画名
runAnim: "run", // 跑步动画名
jumpAnim: "jump", // 跳跃动画名;或传 ["起跳", "循环", "落地"] 分三段播放
},
// 人物初始出生点
initPos: new THREE.Vector3(0, 0, 0),
// 初始化时注册的碰撞体
colliders: [
{
motion: "static", // 静态碰撞体
shape: { // 碰撞形状配置
kind: "mesh", // 使用 Mesh 作为碰撞形状
source: scene, // 从该对象收集几何并自动构建 BVH
},
},
],
});
function animate() {
requestAnimationFrame(animate);
player.update();
renderer.render(scene, camera);
}
animate();
player.update()内部已经接管传入的相机控制器。渲染循环中不要再次调用controls.update(),否则可能与内部相机逻辑冲突。
完整参数示例
init()
await player.init({
// 必填
scene, // three.js 场景实例
camera, // three.js 相机实例
controls, // 外部相机控制器,通常为 OrbitControls
playerModelConfig: { // 人物模型、动画与移动参数
model: playerGltf.scene, // 外部加载后的模型根节点
animations: playerGltf.animations, // 动画片段
scale: 0.001, // 模型缩放
idleAnim: "idle", // 静止动画名
walkAnim: "walk", // 行走动画名
runAnim: "run", // 跑步动画名
jumpAnim: "jump", // 跳跃动画名;或传 ["起跳", "循环", "落地"] 分三段播放
// 方向 / 飞行动画(可选,不填则复用对应默认动画)
leftWalkAnim: "leftWalk", // 左移动画,默认复用 walkAnim
rightWalkAnim: "rightWalk", // 右移动画,默认复用 walkAnim
backwardAnim: "walkBack", // 后退动画,默认复用 walkAnim
flyAnim: "fly", // 飞行动画,默认复用 idleAnim
flyIdleAnim: "flyIdle", // 飞行待机动画,默认复用 idleAnim
flyHoverForwardAnim: "flyFwd", // 飞行前进悬停动画,默认复用 flyAnim
flyHoverBackAnim: "flyBack", // 飞行后退悬停动画,默认复用 flyIdleAnim
flyHoverLeftAnim: "flyLeft", // 飞行左移悬停动画,默认复用 flyIdleAnim
flyHoverRightAnim: "flyRight", // 飞行右移悬停动画,默认复用 flyIdleAnim
flyHoverUpAnim: "flyUp", // 飞行上升悬停动画,默认复用 flyIdleAnim
flyHoverDownAnim: "flyDown", // 飞行下降悬停动画,默认复用 flyIdleAnim
drivingAnim: "driving", // 驾驶循环动画,默认复用 idleAnim
// 物理参数(可选)
gravity: -2400, // 重力基准值,会按 scale 缩放,默认 -2400
jumpHeight: 600, // 跳跃高度基准值,会按 scale 缩放,默认 600
speed: 200, // 移动速度基准值,会按 scale 缩放,默认 200
runSpeed: 600, // 跑步速度基准值,会按 scale 缩放,默认 600
flySpeed: 2100, // 飞行速度基准值,会按 scale 缩放,默认 2100
acceleration: 30, // XZ 加速响应速度,默认 30
deceleration: 30, // XZ 减速响应速度,默认 30
// 模型参数(可选)
rotateY: 0, // 人物初始朝向(弧度),默认 0
headBoneName: "Head", // 头部骨骼名,用于第一人称相机挂载
firstPersonCameraOffset: [0, 40, 30], // 第一人称相机局部偏移
capsuleRadiusRatio: 1, // 胶囊体半径倍率,默认 1
},
// 场景与碰撞(可选)
initPos: new THREE.Vector3(0, 0, 0), // 初始出生点,默认 (0, 0, 0)
colliders: [ // 初始化时批量创建的碰撞体;不传则可在 init() 后调用 addCollider()
{
motion: "static", // 碰撞体运动类型:static / kinematic / dynamic
shape: { // 碰撞形状配置
kind: "mesh", // Mesh 碰撞形状
source: world, // 碰撞来源,可传 THREE.Object3D 或 THREE.Object3D[]
},
useWorker: false, // 是否使用 Worker 异步构建 BVH
},
],
// 相机(可选)
minCamDistance: 100, // 第三人称最小镜头距离,默认 100
maxCamDistance: 440, // 第三人称最大镜头距离,默认 440
camLookAtHeightRatio: 0.8, // 相机看向点高度比例,0=底部,1=顶部,默认 0.8
thirdMouseMode: 1, // 第三人称鼠标控制模式 0-5,默认 1
enableZoom: false, // 是否允许滚轮缩放,默认 false
enableOverShoulderView: false, // 是否启用过肩视角,默认 false
camOverShoulderOffsetRatio: 0.2, // 过肩视角横向偏移比例,默认 0.2
isFirstPerson: false, // 初始是否进入第一人称,默认 false
enableSpringCamera: false, // 是否启用弹簧相机,默认 false
springCameraTime: 0.05, // 弹簧相机平滑时间(秒),默认 0.05
// 其他(可选)
mouseSensitivity: 5, // 鼠标灵敏度,默认 5
timeScale: 1, // 时间缩放系数,<1 慢动作,>1 快进,默认 1
keyMap: { // 自定义键位;可传单键、数组多绑或 null 禁用
forward: ["KeyW", "ArrowUp"], // 前进
backward: ["KeyS", "ArrowDown"], // 后退
left: ["KeyA", "ArrowLeft"], // 左移
right: ["KeyD", "ArrowRight"], // 右移
sprint: ["ShiftLeft", "ShiftRight"], // 冲刺;开车时为手刹
jump: ["Space"], // 跳跃;开车时为刹车
toggleView: ["KeyV"], // 切换第一 / 第三人称
toggleFly: ["KeyF"], // 切换飞行模式
toggleVehicle: ["KeyE"], // 上 / 下车
},
isShowMobileControls: true, // 移动端是否显示虚拟控制 UI,默认 true
mobileControls: { // 移动端按钮显隐、图标与布局配置
joystick: true, // 是否显示虚拟摇杆,默认 true
jump: { // 跳跃 / 刹车按钮;传 false 可隐藏
icon: "/icons/custom-jump.svg", // 跳跃状态自定义图片 URL
brakeIcon: "/icons/custom-brake.svg", // 车辆模式下刹车状态自定义图片 URL
right: 24, // 距离右侧位置(px)
bottom: 32, // 距离底部位置(px)
size: 64, // 按钮直径(px),默认 56
},
fly: true, // 飞行按钮;true 使用默认样式,false 隐藏
view: true, // 视角切换按钮;true 使用默认样式,false 隐藏
vehicle: true, // 上 / 下车按钮;true 使用默认样式,false 隐藏
},
});碰撞系统
使用统一的 CollisionWorld 和 addCollider() API。
碰撞体由两个核心属性描述:
{
motion: "static" | "kinematic" | "dynamic",
shape: {
kind: "mesh" | "box" | "sphere",
// ...
}
}静态 Mesh
适用于地形、建筑、墙体和不会移动的场景模型。
const handle = player.addCollider({
motion: "static",
shape: {
kind: "mesh",
source: world,
},
useWorker: true,
});运动学 Mesh
适用于电梯、移动平台、旋转平台、车辆外观或其它由业务代码控制变换的物体。
const platformCollider = player.addCollider({
motion: "kinematic",
shape: {
kind: "mesh",
source: platform,
},
follow: platform,
useWorker: true,
});碰撞体会在每帧跟随 follow.matrixWorld,运行时如果只需要调整已经烘焙的运动学碰撞几何,可以避免重建 BVH。
动态刚体
动态 box 和 sphere 会自动创建内置刚体,由 DynamicBodySystem 进行重力、阻尼、接触、冲量和休眠计算。
动态盒体
const box = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial(),
);
scene.add(box);
player.addCollider({
motion: "dynamic",
shape: {
kind: "box",
halfExtents: new THREE.Vector3(0.5, 0.5, 0.5),
position: new THREE.Vector3(0, 5, 0),
},
mesh: box,
});动态球体
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.5),
new THREE.MeshStandardMaterial(),
);
scene.add(sphere);
player.addCollider({
motion: "dynamic",
shape: {
kind: "sphere",
radius: 0.5,
position: new THREE.Vector3(0, 5, 0),
},
mesh: sphere,
});动态刚体支持与静态 Mesh、运动学 Mesh、人物、车辆以及其它动态刚体发生碰撞。
移除与清理
const handle = player.addCollider({
motion: "static",
shape: { kind: "mesh", source: object },
});
player.removeCollider(handle);
// 只清除静态碰撞体
player.clearColliders({ motion: "static" });
// 清除全部碰撞体
player.clearColliders();碰撞分组
import { CollisionGroup } from "three-player-controller";| 分组 | 说明 |
| --- | --- |
| CollisionGroup.DEFAULT | 默认场景碰撞。 |
| CollisionGroup.CHARACTER | 玩家角色碰撞组。 |
| CollisionGroup.VEHICLE | 车辆碰撞组。 |
| CollisionGroup.DEBRIS | 动态杂物、可推动物体等碰撞组。 |
| CollisionGroup.ALL | 所有碰撞组的组合,表示允许与全部分组发生碰撞。 |
默认情况下无需设置,会根据 motion 自动使用对应的碰撞组和掩码;也可以通过 groups 和 mask 自定义碰撞过滤:
player.addCollider({
motion: "dynamic",
shape: {
kind: "sphere",
radius: 0.5,
position: new THREE.Vector3(),
},
groups: CollisionGroup.DEBRIS,
mask: CollisionGroup.DEFAULT | CollisionGroup.VEHICLE,
});3D Tiles 流式碰撞
对于 3D Tiles,不建议把整个 Tileset 一次性合并成一个碰撞体。
example/3dtilesScene.js 使用流式策略:
- 监听瓦片加载、卸载和可见性变化。
- 只跟踪当前已加载瓦片。
- 只为人物附近且可见的瓦片创建碰撞体。
- 使用
useWorker: true异步构建 BVH。 - 瓦片隐藏、卸载或距离过远时立即
removeCollider()。 - 使用进入 / 退出双半径,避免边界附近频繁创建和销毁。
核心形式:
const handle = player.addCollider({
motion: "kinematic",
useWorker: true,
shape: {
kind: "mesh",
source: tileScene,
},
follow: tileScene,
});
// 瓦片卸载 / 不可见 / 距离过远时移除碰撞体
player.removeCollider(handle);示例同时演示了地球级大坐标场景中的局部坐标系和 rebase:人物距离局部原点过远时更新局部坐标系,并同步角色、相机和速度,避免大坐标浮点精度问题。
完整实现见 example/3dtilesScene.js。
车辆
const vehicleGltf = await loader.loadAsync("./glb/vehicle.glb"); // 外部加载车辆模型
const vehicle = await player.loadVehicleModel({
// 必填
model: vehicleGltf.scene, // 外部加载后的车辆模型根节点
position: new THREE.Vector3(0, 0, 0), // 车辆初始世界坐标
wheelsNames: [ // 车轮节点名,顺序:左前、右前、左后、右后
"Wheel_LF", // 左前轮节点名
"Wheel_RF", // 右前轮节点名
"Wheel_LR", // 左后轮节点名
"Wheel_RR", // 右后轮节点名
],
driverSeatPosition: new THREE.Vector3(-0.6, 0.7, 0.4), // 驾驶位胶囊中心,使用底盘局部坐标
// 模型 / 驾驶(可选)
scale: 0.1, // 车辆模型缩放,默认 1
driverSeatRotation: 0, // 驾驶位相对底盘局部的水平旋转(弧度),默认 0
modelRotation: -Math.PI / 2, // 车辆模型绕 Y 轴旋转(弧度),默认 -π/2
followVehicleDirection: true, // 驾驶时镜头是否跟随车辆朝向,默认 true
// 调试(可选)
debug: { // 车辆物理调试显示配置
showPhysicsBox: false, // 是否显示底盘物理碰撞盒,默认 false
showWheelRays: false, // 是否显示车轮悬挂射线,默认 false
showWheelTravel: false, // 是否显示车轮 Y 向活动范围,默认 false
showWheelSpheres: false, // 是否显示车轮动态碰撞球,默认 false
},
// 底盘(可选)
chassis: { // 底盘碰撞盒与阻尼参数
density: 1, // 底盘密度,质量根据碰撞盒体积与密度计算,默认 1
linearDamping: 0.05, // 线阻尼,默认 0.05
angularDamping: 0.5, // 角阻尼,默认 0.5
clearance: 0.15, // 底盘盒底相对轮胎触地点高度,不传时自动计算
sizeScale: { // 相对自动 AABB 的底盘碰撞盒尺寸倍率
x: 1, // X 轴尺寸倍率,默认 1
y: 1, // Y 轴尺寸倍率,默认 1
z: 1, // Z 轴尺寸倍率,默认 1
},
},
// 悬挂 / 轮胎(可选)
suspension: { // 悬挂与轮胎参数
restLength: 0.3, // 悬挂静止长度,不传时自动计算
maxTravel: 0.35, // 悬挂最大行程,默认 0.35
stiffness: 18, // 悬挂刚度,默认 18
compression: 2.1, // 悬挂压缩阻尼,默认 2.1
relaxation: 2.5, // 悬挂回弹阻尼,默认 2.5
maxForce: 6000, // 单轮最大悬挂力,默认 6000
frictionSlip: 8, // 轮胎纵向抓地,默认 8
sideFrictionStiffness: 1, // 轮胎侧向摩擦刚度,默认 1
rollInfluence: 0.12, // 侧向力对车身侧倾的影响,默认 0.12
},
// 转向(可选)
steering: { // 转向参数
maxSteerAngle: Math.PI / 5, // 最大转向角(弧度),默认 π/5
steerTime: 0.45, // 从中位打到满舵所需时间(秒),默认 0.45
steerReturnTimeSlow: 0.55, // 低速回正时间(秒),默认 0.55
steerReturnTimeFast: 0.4, // 高速回正时间(秒),默认 0.4
highSpeedSteerScale: 0.3, // 最高车速时最大转向比例,默认 0.3
},
// 抓地 / 手刹(可选)
grip: { // 过弯抓地与手刹参数
maxG: 1.2, // 最大侧向加速度(g),默认 1.2
sideFrictionIdle: 1, // 直线行驶时侧向摩擦,默认 1
sideFrictionFrontMin: 0.55, // 高负载下前轮侧向摩擦下限,默认 0.55
sideFrictionRearMin: 0.45, // 高负载下后轮侧向摩擦下限,默认 0.45
handbrakeRearFriction: 0.35, // 手刹时后轮侧向摩擦,默认 0.35
handbrakeRearDriveScale: 0.65, // 手刹时后轮驱动力比例,默认 0.65
handbrakeReleaseTime: 0.15, // 松开手刹后的恢复时间(秒),默认 0.15
wheelbaseRatio: 0.55, // 轴距相对车长比例,默认 0.55
},
// 动力(可选)
power: { // 车辆动力参数
maxSpeed: 100, // 最高速度基准值(km/h),会按 scale 缩放,默认 100
acceleration: 5, // 加速度基准值(m/s²),会按 scale 缩放,默认 5
deceleration: 5, // 制动减速度基准值(m/s²),会按 scale 缩放,默认 5
},
});运行时:
player.resetVehicle(); // 翻正当前驾驶车辆,并清零线速度和角速度
player.setVehicleScale(vehicle, 0.12); // 动态修改车辆整体缩放
player.setVehicleClearance(vehicle, 0.15); // 调整底盘碰撞盒离地间隙
player.setVehicleChassisSizeScale(vehicle, { // 调整底盘碰撞盒相对自动 AABB 的尺寸倍率
x: 1, // X 轴尺寸倍率
y: 0.9, // Y 轴尺寸倍率
z: 1, // Z 轴尺寸倍率
});
player.setVehiclePhysicsDebug(true); // 显示 / 隐藏车辆底盘物理碰撞盒Foot IK 插件
import { FootIK } from "three-player-controller/foot-ik";
const footIK = new FootIK({
// 手动指定人物骨骼映射;不传时会尝试自动匹配常见骨骼名
skeleton: {
hips: "mixamorigHips", // 髋部 / 骨盆骨骼名
// 左右腿骨骼配置
legs: {
left: {
upper: "mixamorigLeftUpLeg", // 左大腿骨骼
lower: "mixamorigLeftLeg", // 左小腿骨骼
foot: "mixamorigLeftFoot", // 左脚骨骼
toe: "mixamorigLeftToeBase", // 左脚趾骨骼
},
right: {
upper: "mixamorigRightUpLeg", // 右大腿骨骼
lower: "mixamorigRightLeg", // 右小腿骨骼
foot: "mixamorigRightFoot", // 右脚骨骼
toe: "mixamorigRightToeBase", // 右脚趾骨骼
},
},
},
soleSkinThickness: 3, // 脚骨到鞋底的蒙皮厚度补偿
});
player.use(footIK); // 将 Foot IK 插件注册到玩家控制器运行时:
footIK.setEnabled(false); // 启用 / 禁用 Foot IK
footIK.setDebugEnabled(true); // 显示 / 隐藏 Foot IK 调试辅助
player.unuse(footIK); // 从玩家控制器中卸载 Foot IK 插件
footIK.dispose(); // 释放 Foot IK 创建的调试对象和相关资源player.destroy() 会自动销毁已注册插件。
完整示例见 example/footIK.js。
API
生命周期与核心
| 方法 | 说明 |
| --- | --- |
| init(opts, callback?) | 初始化控制器。 |
| update(dt?) | 每帧更新人物、碰撞、动态刚体、车辆、相机和动画。 |
| destroy() | 销毁控制器并释放资源。 |
| reset(pos?) | 将角色重置到指定位置或初始位置。 |
| switchPlayerModel(options) | 使用 PlayerModelOptions 运行时切换角色模型。 |
| use(plugin) | 注册插件。 |
| unuse(plugin) | 卸载插件。 |
| getPlugins() | 获取当前插件列表副本。 |
碰撞
| 方法 | 说明 |
| --- | --- |
| addCollider(desc) | 添加静态 / 运动学 / 动态碰撞体,返回 ColliderHandle。 |
| removeCollider(handle) | 按句柄或 id 移除碰撞体。 |
| clearColliders(filter?) | 清除碰撞体,可按 motion 过滤。 |
| getColliderMeshes(options?) | 获取当前人物查询使用的 Mesh 碰撞体。 |
| isStaticColliderUsable() | 是否存在已经完成 BVH 构建的静态 Mesh。 |
| getActiveKinematicCollider() | 获取当前角色站立的运动学碰撞体。 |
| getActiveDynamicBody() | 获取当前角色站立的动态刚体。 |
| getDynamicBodies() | 获取全部动态刚体。 |
| removeDynamicBody(body) | 移除动态刚体。 |
| clearDynamicBodies() | 清除全部动态刚体。 |
| raycastDynamicGround(...) | 向下查询动态刚体表面。 |
状态
| 方法 | 返回内容 |
| --- | --- |
| getPosition() | 当前角色位置。 |
| getVelocity() | 当前角色速度。 |
| getIsFirstPerson() | 是否第一人称。 |
| getIsFlying() | 是否飞行。 |
| getIsOnGround() | 是否在地面。 |
| getGroundSupport() | 当前最终地面支撑点和世界空间法线。 |
| getCurrentDelta() | 当前帧实际使用的时间步长。 |
| getControllerMode() | 0 人物,1 车辆。 |
| getPlayerModel() | 当前人物模型。 |
| getPlayerCapsule() | 玩家胶囊体。 |
| getActiveVehicle() | 当前驾驶车辆。 |
| getAllVehicles() | 所有已加载车辆。 |
| getCurrentPlayerAnimationName() | 当前动画片段名。 |
| getCurrentLocomotionSet() | 当前 Locomotion Set。 |
| getCenterScreenRaycastHit() | 屏幕中心射线结果。 |
运行时控制
| 方法 | 说明 |
| --- | --- |
| setInput(input) | 注入外部输入。 |
| setKeyMap(map?) | 自定义或恢复默认键位。 |
| setMouseSensitivity(v) | 设置鼠标灵敏度。 |
| setPlayerScale(v) | 动态修改人物缩放。 |
| setPlayerSpeed(v) | 设置行走速度。 |
| setPlayerRunSpeed(v) | 设置跑步速度。 |
| setPlayerFlySpeed(v) | 设置飞行速度。 |
| setJumpHeight(v) | 设置跳跃高度。 |
| setGravity(v) | 设置重力。 |
| setMinCamDistance(v) | 设置第三人称最小镜头距离。 |
| setMaxCamDistance(v) | 设置第三人称最大镜头距离。 |
| setCamLookAtHeightRatio(v) | 设置相机看向点高度比例。 |
| setCamOverShoulderOffsetRatio(v) | 设置过肩横向偏移比例。 |
| setThirdMouseMode(v) | 设置第三人称鼠标模式。 |
| setEnableZoom(v) | 开关滚轮缩放。 |
| setOverShoulderView(v) | 开关过肩视角。 |
| setEnableToward(v) | 开关鼠标驱动朝向。 |
| setSkipCapsuleCollision(v) | 临时跳过人物胶囊碰撞。 |
| changeView() | 切换第一 / 第三人称。 |
| setFirstPersonCamera(v?) | 直接进入第一人称。 |
调试
player.setColliderDebug(true); // 显示 / 隐藏静态与运动学 Mesh 碰撞线框
player.setPlayerCapsuleDebug(true); // 显示 / 隐藏玩家胶囊体线框
player.setDynamicBodyDebug(true); // 显示 / 隐藏动态刚体碰撞线框
player.setVehiclePhysicsDebug(true); // 显示 / 隐藏车辆底盘物理碰撞盒动画
| 方法 | 说明 |
| --- | --- |
| playPlayerAnimationByName(name, fade?) | 按动画片段名直接播放角色动画。 |
| registerAnimation(key, clipName, opts?) | 注册自定义动画片段。 |
| playAnimation(key, opts?) | 播放已注册的自定义动画。 |
| registerLocomotionSet(setName, map) | 注册一套移动动画集合,用于替换内置移动动画。 |
| switchLocomotionSet(setName, fade?) | 切换到指定的移动动画集合。 |
registerAnimation
player.registerAnimation(key, clipName, {
loop?: boolean, // 是否循环播放,默认 true
timeScale?: number, // 动画播放放缩,默认 1
duration?: number, // 动画播放时长,默认 0
clampWhenFinished?: boolean, // 是否在播放结束后将动画时间重置为 0,默认 false
onFinished?: () => void, // 动画播放结束后触发
});duration 与 timeScale 同时设置时,duration 优先生效。
playAnimation
player.playAnimation(key, {
fade?: number, // 过渡时长(秒),默认 0.18
force?: boolean, // 为 true 时强制从头重播,即使当前已在播放该动画
returnToPrev?: boolean, // 仅对 LoopOnce 动画生效,播放结束后自动恢复上一个动画状态
});registerLocomotionSet
支持的 key:idle | walking | walking_backward | running | jumping | flyidle | flying,填写的 key 会替换对应内置动画,未填的保持原有动画。
player.registerLocomotionSet("combat", {
idle: "CombatIdle",
walking: "CombatWalk",
walking_backward: "CombatBack",
running: "CombatRun",
jumping: "CombatJump",
flyidle: "CombatFlyIdle",
flying: "CombatFly",
});默认键位
| 动作 | 默认键 | 功能 |
| --- | --- | --- |
| 前进 | W / ArrowUp | 前进 |
| 后退 | S / ArrowDown | 后退 |
| 左移 | A / ArrowLeft | 左移 |
| 右移 | D / ArrowRight | 右移 |
| 冲刺 | Shift | 人物冲刺;车辆手刹 |
| 跳跃 | Space | 跳跃;飞行上升;车辆刹车 |
| 切换视角 | V | 第一 / 第三人称 |
| 飞行 | F | 开关飞行 |
| 上 / 下车 | E | 车辆交互 |
| 视角 | 鼠标 | 控制视角 |
自定义键位
通过 keyMap 可以把上表的任意动作改绑到其它按键,或禁用某个动作。键名使用 KeyboardEvent.code(如 "KeyE"、"ArrowUp"、"Space",注意是 "KeyE" 而非 "e")。
每个动作三种取值:
- 不填 → 使用默认键
- 字符串 / 字符串数组 → 替换为指定按键(数组可绑定多个键)
null→ 禁用该动作(任何键都不会触发)
初始化时配置:
await player.init({
// ...
keyMap: {
forward: "KeyE", // 改为按 E 前进(替换默认 W / ↑)
jump: null, // 禁用跳跃
left: ["KeyA", "KeyJ"], // 同时绑定 A 和 J
// 其余动作未填,保持默认键
},
});运行时切换键位方案:
player.setKeyMap({ forward: "KeyI", backward: "KeyK" }); // 应用新键位
player.setKeyMap(); // 恢复全部默认外部输入
player.setInput({
moveX: number, // 水平移动轴,范围 -1~1,正数向右
moveY: number, // 纵向移动轴,范围 -1~1,正数向前
lookDeltaX: number, // 视角水平增量,通常来自 mousemove 的 movementX
lookDeltaY: number, // 视角垂直增量,通常来自 mousemove 的 movementY
jump: boolean, // 跳跃,持续状态(true=按下,false=松开);飞行时控制上升;开车时为刹车
shift: boolean, // 人物冲刺,开车时为手刹,持续状态(true=按下,false=松开)
toggleView: boolean, // 触发式,传 true 切换第一/第三人称视角
toggleFly: boolean, // 触发式,传 true 切换飞行模式
toggleVehicle: boolean, // 触发式,传 true 上车 / 下车
});事件
player.onAnimationChange = (name, action) => {}; // 角色当前动画切换时触发
player.onBeforeViewChange = (isFirstPerson) => {}; // 第一 / 第三人称切换前触发
player.onViewChange = (isFirstPerson) => {}; // 第一 / 第三人称切换后触发
player.onGroundChange = (onGround) => {}; // 落地状态变化时触发
player.onVehicleEnter = (vehicle) => {}; // 上车完成后触发
player.onVehicleExit = (vehicle) => {}; // 下车完成后触发
player.onTowardChange = (dx, dy, speed) => {}; // 朝向 / 视角输入更新时触发主要配置
PlayerControllerOptions
| 字段 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| scene | THREE.Scene | 是 | — | three.js 场景实例。 |
| camera | THREE.PerspectiveCamera | 是 | — | three.js 相机实例。 |
| controls | any | 是 | — | 外部相机控制器,通常为 OrbitControls。 |
| playerModelConfig | PlayerModelOptions | 是 | — | 角色模型与参数配置。 |
| initPos | THREE.Vector3 | 否 | (0, 0, 0) | 初始出生点。 |
| colliders | ColliderDesc[] | 否 | — | 初始化时批量注册碰撞体。 |
| mouseSensitivity | number | 否 | 5 | 鼠标灵敏度。 |
| minCamDistance | number | 否 | 100 | 第三人称最小镜头距离。 |
| maxCamDistance | number | 否 | 440 | 第三人称最大镜头距离。 |
| isShowMobileControls | boolean | 否 | true | 是否在移动端显示虚拟控制 UI。 |
| mobileControls | MobileControlsOptions | 否 | 全部显示 | 移动端按钮显隐、图标与布局配置。 |
| thirdMouseMode | 0 \| 1 \| 2 \| 3 \| 4 \| 5 | 否 | 1 | 第三人称视角下的鼠标控制模式(0:隐藏鼠标,控制朝向及视角;1:隐藏鼠标,仅控制视角;2:显示鼠标,拖拽控制朝向及视角;3:显示鼠标,拖拽仅控制视角;4:显示鼠标,拖拽控制视角且人物朝向跟随相机水平方向;5:隐藏鼠标,控制视角且人物朝向跟随相机水平方向) |
| enableZoom | boolean | 否 | false | 是否允许滚轮缩放。 |
| enableOverShoulderView | boolean | 否 | false | 是否启用过肩视角。 |
| camOverShoulderOffsetRatio | number | 否 | 0.2 | 第三人称相机过肩视角横向偏移比例。 |
| isFirstPerson | boolean | 否 | false | 初始化时是否直接进入第一人称。 |
| enableSpringCamera | boolean | 否 | false | 是否启用弹簧相机(目标点以弹簧阻尼跟随角色)。 |
| springCameraTime | number | 否 | 0.05 | 弹簧平滑时间(秒),越小跟随越紧。 |
| camLookAtHeightRatio | number | 否 | 0.8 | 第三人称相机看向点高度比例(0=胶囊底部,1=顶部)。 |
| timeScale | number | 否 | 1 | 时间缩放系数,<1 慢动作,>1 快进。 |
| keyMap | KeyMap | 否 | 默认键位 | 自定义键位映射,详见自定义键位。 |
PlayerModelOptions
| 字段 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| model | THREE.Object3D | 二选一 | — | 已加载的人物模型根节点。与 url 互斥。 |
| animations | THREE.AnimationClip[] | 与 model 同用 | — | 人物动画片段。与 model 一起传入。 |
| url | string | 二选一 | — | 人物模型路径(GLB/GLTF)。 |
| scale | number | 是 | — | 人物模型缩放。 |
| idleAnim | string | 是 | — | Idle 动画名。 |
| walkAnim | string | 是 | — | Walk 动画名。 |
| runAnim | string | 是 | — | Run 动画名。 |
| jumpAnim | string \| [string, string, string] | 是 | — | 跳跃动画名。传字符串播放单一动画;传三元素数组 [起跳, 循环, 落地] 分三阶段播放,落地后自动衔接移动动画。 |
| leftWalkAnim | string | 否 | walkAnim | 左移动画名,不填则复用 walkAnim。 |
| rightWalkAnim | string | 否 | walkAnim | 右移动画名,不填则复用 walkAnim。 |
| backwardAnim | string | 否 | walkAnim | 后退动画名,不填则复用 walkAnim。 |
| flyAnim | string | 否 | idleAnim | 飞行动画名,不填则复用 idleAnim。 |
| flyIdleAnim | string | 否 | idleAnim | 飞行待机动画名,不填则复用 idleAnim。 |
| flyHoverForwardAnim | string | 否 | flyAnim | 飞行前进时的悬停动画名,不填则复用 flyAnim。 |
| flyHoverBackAnim | string | 否 | flyIdleAnim | 飞行后退时的悬停动画名,不填则复用 flyIdleAnim。 |
| flyHoverLeftAnim | string | 否 | flyIdleAnim | 飞行左移时的悬停动画名,不填则复用 flyIdleAnim。 |
| flyHoverRightAnim | string | 否 | flyIdleAnim | 飞行右移时的悬停动画名,不填则复用 flyIdleAnim。 |
| flyHoverUpAnim | string | 否 | flyIdleAnim | 飞行上升时的悬停动画名,不填则复用 flyIdleAnim。 |
| flyHoverDownAnim | string | 否 | flyIdleAnim | 飞行下降时的悬停动画名,不填则复用 flyIdleAnim。 |
| drivingAnim | string | 否 | idleAnim | 驾驶循环动画名,不填则复用 idleAnim。 |
| gravity | number | 否 | -2400 | 重力基准值(按 scale 缩放)。 |
| jumpHeight | number | 否 | 600 | 跳跃高度基准值(按 scale 缩放)。 |
| speed | number | 否 | 200 | 移动速度基准值(按 scale 缩放)。 |
| runSpeed | number | 否 | 600 | 跑步速度基准值(按 scale 缩放)。 |
| flySpeed | number | 否 | 2100 | 飞行速度基准值(按 scale 缩放)。 |
| rotateY | number | 否 | 0 | 人物初始朝向(弧度),用于改变模型初始化面朝方向。 |
| headBoneName | string | 否 | — | 头部骨骼或节点名称,用于第一人称相机挂载。 |
| firstPersonCameraOffset | [number, number, number] | 否 | 有内置默认值 | 第一人称相机局部偏移;有 headBoneName 则相对头骨骼,否则相对胶囊体。 |
| capsuleRadiusRatio | number | 否 | 1 | 胶囊体半径倍率,用于微调碰撞宽度。 |
| acceleration | number | 否 | 30 | XZ 方向加速响应速度,值越大加速越快。 |
| deceleration | number | 否 | 30 | XZ 方向减速响应速度,值越大停止越快。 |
MobileControlsOptions
| 字段 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| joystick | boolean | 否 | true | 是否显示支持 360° 连续方向的摇杆。 |
| jump | boolean \| JumpButtonOptions | 否 | true | 跳跃/刹车按钮;false 隐藏,对象可自定义图片和布局。 |
| fly | boolean \| MobileButtonOptions | 否 | true | 飞行按钮;false 隐藏,对象可自定义图片和布局。 |
| view | boolean \| MobileButtonOptions | 否 | true | 视角切换按钮;false 隐藏,对象可自定义图片和布局。 |
| vehicle | boolean \| MobileButtonOptions | 否 | true | 上下车按钮;false 隐藏,对象可自定义图片和布局。 |
MobileButtonOptions / JumpButtonOptions
| 字段 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| left / right | number | 否 | 内置位置 | 按钮距离左侧或右侧的位置(px)。设置 left 且未设置 right 时会清除默认右侧定位。 |
| top / bottom | number | 否 | 内置位置 | 按钮距离顶部或底部的位置(px)。设置 top 且未设置 bottom 时会清除默认底部定位。 |
| size | number | 否 | 56 | 圆形按钮直径(px)。 |
| icon | string | 否 | 英文标签 | 自定义图片 URL。 |
| brakeIcon | string | 否 | BRAKE 标签 | 仅用于 JumpButtonOptions,设置车辆模式下的刹车图片 URL。 |
ColliderDesc
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| motion | "static" \| "kinematic" \| "dynamic" | — | 碰撞体运动类型。 |
| shape | ColliderShape | — | 碰撞形状配置,支持 mesh、box、sphere。 |
| groups | number | 根据 motion 自动设置 | 当前碰撞体所属碰撞组。 |
| mask | number | 根据 motion 自动设置 | 当前碰撞体允许发生碰撞的分组掩码。 |
| useWorker | boolean | false | mesh + source 时是否使用 Worker 异步构建 BVH。 |
| userData | unknown | — | 自定义业务数据。 |
| follow | THREE.Object3D | — | kinematic 碰撞体跟随的场景对象。 |
| density | number | 1 | dynamic 刚体密度,用于计算质量。 |
| restitution | number | 0.2 | dynamic 刚体弹性系数。 |
| friction | number | 0.6 | dynamic 刚体摩擦系数。 |
| linearDamping | number | 0.4 | dynamic 刚体线阻尼。 |
| angularDamping | number | 0.6 | dynamic 刚体角阻尼。 |
| gravity | number | 内部默认值 | dynamic 刚体重力。 |
| velocity | THREE.Vector3 | (0,0,0) | dynamic 刚体初始线速度。 |
| angularVelocity | THREE.Vector3 | (0,0,0) | dynamic 刚体初始角速度。 |
| mesh | THREE.Mesh | — | dynamic box / sphere 创建内置刚体时必须提供的可视化 Mesh。 |
| simulate | boolean | true | 是否由内置动态刚体系统进行模拟。 |
ColliderShape
Mesh
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| kind | "mesh" | Mesh 碰撞形状。 |
| source | THREE.Object3D \| THREE.Object3D[] | 从对象中收集几何、合并碰撞网格并构建 BVH。 |
| mesh | THREE.Mesh | 直接注册已经准备好的碰撞 Mesh。 |
Box
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| kind | "box" | 盒体碰撞形状。 |
| halfExtents | THREE.Vector3 | 盒体半尺寸,即完整尺寸的一半。 |
| position | THREE.Vector3 | 初始世界坐标,可选。 |
| quaternion | THREE.Quaternion | 初始世界旋转,可选。 |
Sphere
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| kind | "sphere" | 球体碰撞形状。 |
| radius | number | 球体半径。 |
| position | THREE.Vector3 | 初始世界坐标,可选。 |
VehicleOptions
基础参数
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| model | THREE.Object3D | — | 外部加载后的车辆模型根节点。 |
| position | THREE.Vector3 | — | 车辆初始世界坐标。 |
| wheelsNames | [string, string, string, string] | — | 车轮节点名数组,顺序为左前、右前、左后、右后。 |
| driverSeatPosition | THREE.Vector3 | — | 驾驶位胶囊中心,车辆底盘局部坐标。 |
| scale | number | 1 | 车辆模型缩放。 |
| driverSeatRotation | number | 0 | 驾驶位相对车辆底盘局部的水平旋转(弧度)。 |
| modelRotation | number | -Math.PI / 2 | 车辆模型绕 Y 轴的初始旋转。 |
| followVehicleDirection | boolean | true | 驾驶时镜头是否跟随车辆朝向。 |
| debug | VehicleDebugOptions | — | 车辆物理调试显示配置。 |
| chassis | VehicleChassisOptions | — | 底盘碰撞盒与阻尼配置。 |
| suspension | VehicleSuspensionOptions | — | 悬挂与轮胎配置。 |
| steering | VehicleSteeringOptions | — | 转向配置。 |
| grip | VehicleGripOptions | — | 抓地和手刹配置。 |
| power | VehiclePowerOptions | — | 动力配置。 |
debug
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| showPhysicsBox | boolean | false | 显示底盘物理碰撞盒。 |
| showWheelRays | boolean | false | 显示车轮悬挂射线。 |
| showWheelTravel | boolean | false | 显示车轮 Y 向活动范围。 |
| showWheelSpheres | boolean | false | 显示车轮动态碰撞球。 |
chassis
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| density | number | 1 | 底盘密度,质量根据碰撞盒体积与密度计算。 |
| linearDamping | number | 0.05 | 底盘线阻尼。 |
| angularDamping | number | 0.5 | 底盘角阻尼。 |
| clearance | number | 自动计算 | 底盘盒底相对轮胎触地点高度。 |
| sizeScale.x | number | 1 | 底盘碰撞盒 X 轴尺寸倍率。 |
| sizeScale.y | number | 1 | 底盘碰撞盒 Y 轴尺寸倍率。 |
| sizeScale.z | number | 1 | 底盘碰撞盒 Z 轴尺寸倍率。 |
suspension
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| restLength | number | 自动计算 | 悬挂静止长度。 |
| maxTravel | number | 0.35 | 悬挂最大行程。 |
| stiffness | number | 18 | 悬挂刚度。 |
| compression | number | 2.1 | 悬挂压缩阻尼。 |
| relaxation | number | 2.5 | 悬挂回弹阻尼。 |
| maxForce | number | 6000 | 单轮最大悬挂力。 |
| frictionSlip | number | 8 | 轮胎纵向抓地。 |
| sideFrictionStiffness | number | 1 | 轮胎侧向摩擦刚度。 |
| rollInfluence | number | 0.12 | 侧向力对车身侧倾的影响。 |
steering
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| maxSteerAngle | number | Math.PI / 5 | 最大转向角,单位为弧度。 |
| steerTime | number | 0.45 | 从中位打到满舵所需时间,单位为秒。 |
| steerReturnTimeSlow | number | 0.55 | 低速回正时间,单位为秒。 |
| steerReturnTimeFast | number | 0.4 | 高速回正时间,单位为秒。 |
| highSpeedSteerScale | number | 0.3 | 最高车速时的最大转向比例。 |
grip
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| maxG | number | 1.2 | 最大侧向加速度,单位为 g。 |
| sideFrictionIdle | number | 1 | 直线行驶时侧向摩擦。 |
| sideFrictionFrontMin | number | 0.55 | 高负载下前轮侧向摩擦下限。 |
| sideFrictionRearMin | number | 0.45 | 高负载下后轮侧向摩擦下限。 |
| handbrakeRearFriction | number | 0.35 | 手刹时后轮侧向摩擦。 |
| handbrakeRearDriveScale | number | 0.65 | 手刹时后轮驱动力比例。 |
| handbrakeReleaseTime | number | 0.15 | 松开手刹后的恢复时间,单位为秒。 |
| wheelbaseRatio | number | 0.55 | 轴距相对车长的比例。 |
power
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| maxSpeed | number | 100 | 最高速度基准值(km/h,按 scale 缩放)。 |
| acceleration | number | 5 | 加速度基准值(m/s²,按 scale 缩放)。 |
| deceleration | number | 5 | 制动减速度基准值(m/s²,按 scale 缩放)。 |
反馈
如果你有任何问题或建议,欢迎提交 issue。

