Skip to content

hh-hang/three-player-controller

Repository files navigation

中文 | English

three-player-controller

NPM Package

轻量的第三人称 / 第一人称玩家控制器,开箱即用,基于 three.js 和 three-mesh-bvh 实现人物胶囊体碰撞、BVH 碰撞检测、人物动画、第一/三人称切换与相机避障,利用 three-mesh-bvh 优化碰撞检测性能,大场景下高性能运行。

示例

普通控制

普通控制演示

飞行控制

飞行控制演示

车辆控制

车辆控制演示

移动端控制演示

移动端控制演示

安装

npm install three-player-controller three three-mesh-bvh

可选依赖

如果需要使用车辆控制,请安装 Rapier:

npm install @dimforge/rapier3d-compat

如果需要使用移动端控制,请安装 nipplejs:

npm install nipplejs

本地运行示例

# 克隆仓库
git clone https://github.com/hh-hang/three-player-controller.git

# 安装依赖
npm install

# 运行开发服务器
npm run dev

在浏览器访问 http://localhost:5173/three-player-controller/ 查看示例。

使用

import * as THREE from "three";
import { playerController } from "three-player-controller";

const player = playerController();

// 初始化玩家控制器
await player.init({
    scene, // three.js 场景
    camera, // three.js 相机
    controls, // three.js 控制器
    playerModel: {
        url: "./glb/person.glb", // 模型路径
        scale: 0.001,            // 模型缩放
        idleAnim: "idle",        // Idle 动画名
        walkAnim: "walk",        // Walk 动画名
        runAnim: "run",          // Run 动画名
        jumpAnim: "jump",        // Jump 动画名
    },
    initPos: new THREE.Vector3(0, 0, 0),
});

// 加入车辆控制
await player.loadVehicleModel({
    url: "./glb/bugatti.glb",
    scale: 0.1,
    position: new Vector3(22, 3.69, 14.5),
    wheelsNames: [
        "Wheel_LF", // 前左
        "Wheel_RF", // 前右
        "Wheel_LR", // 后左
        "Wheel_RR", // 后右
    ],
    animations: {
        openDoorAnim: "openDoorLF",
    },
    boardingPoint: new Vector3(0.5, 0, 1.8),
    seatOffset: new Vector3(0, 0.6, 0),
    chassisRatio: 0.15,
    suspensionRestLengthRatio: 0.2,
});

// 渲染循环调用
player.update();

API

一、初始化

导出函数

export function playerController(): {
    init: (opts: PlayerControllerOptions, callback?: () => void) => void;
    loadVehicleModel: (params: VehicleOptions) => void;
    switchPlayerModel: (model: PlayerControllerOptions["playerModel"]) => void;
    changeView: () => void;
    reset: (pos?: THREE.Vector3) => void;
    update: (dt?: number) => void;
    destroy: () => void;
    setInput: (input: PlayerInput) => void;
    getPosition: () => THREE.Vector3;
    getCenterScreenRaycastHit: () => THREE.Intersection | undefined;
    getPerson: () => THREE.Object3D | null;
    getActiveVehicle: () => VehicleInstance | null;
    getAllVehicles: () => VehicleInstance[];
    setMouseSensitivity: (mouseSensity: number) => void;
    setGravity: (gravity: number) => void;
    setJumpHeight: (jumpHeight: number) => void;
    setPlayerSpeed: (playerSpeed: number) => void;
    setPlayerFlySpeed: (playerFlySpeed: number) => void;
    setMinCamDistance: (minCamDistance: number) => void;
    setMaxCamDistance: (maxCamDistance: number) => void;
    setThirdMouseMode: (thirdMouseMode: 0 | 1 | 2 | 3) => void;
    setEnableZoom: (enableZoom: boolean) => void;
    setOverShoulderView: (enable: boolean) => void;
    setPlayerScale: (scale: number) => void;
    setDebug: (debug: boolean) => void;
    getCurrentPersonAnimationName: () => string | null;
    registerAnimation: (key: string, clipName: string, opts?: {
        loop?: boolean;
        timeScale?: number;
        duration?: number;
        clampWhenFinished?: boolean;
        onFinished?: () => void;
    }) => void;
    playAnimation: (key: string, opts?: {
        fade?: number;
        force?: boolean;
    }) => void;
};

方法说明

方法 说明
init(opts, callback?) 初始化控制器,callback 在资源加载完成后调用
loadVehicleModel(params) 加载并初始化一辆车辆,可多次调用加载多辆
switchPlayerModel(model) 运行时切换人物模型,保留当前位置与朝向
changeView() 在第一/第三人称间切换
reset(pos?) 复位玩家到指定位置(默认初始位置)
update(dt?) 每帧调用,驱动物理与动画
destroy() 销毁控制器,释放所有资源
setInput(input) 外部传入输入状态,适用于自定义按键或手柄接入
getPosition() 获取人物当前世界坐标
getCenterScreenRaycastHit() 获取屏幕中心射线与碰撞体的交点,可用于瞄准/交互检测
getPerson() 获取人物模型对象
getActiveVehicle() 获取当前正在驾驶的车辆实例
getAllVehicles() 获取所有已加载的车辆实例数组
setMouseSensitivity(v) 设置鼠标灵敏度
setGravity(v) 设置重力(传入基准值,内部自动乘以 scale)
setJumpHeight(v) 设置跳跃高度(传入基准值,内部自动乘以 scale)
setPlayerSpeed(v) 设置移动速度(传入基准值,内部自动乘以 scale)
setPlayerFlySpeed(v) 设置飞行速度(传入基准值,内部自动乘以 scale)
setMinCamDistance(v) 设置第三人称最小相机距离
setMaxCamDistance(v) 设置第三人称最大相机距离
setThirdMouseMode(v) 设置第三人称鼠标模式(0~3)
setEnableZoom(v) 设置第三人称是否允许滚轮缩放
setOverShoulderView(v) 开启/关闭过肩视角偏移
setPlayerScale(scale) 动态设置人物缩放,同步更新碰撞体与所有相关物理参数
setDebug(v) 开启/关闭碰撞体调试显示
getCurrentPersonAnimationName() 获取当前正在播放的动画名称
registerAnimation(key, clipName, opts?) 注册一个自定义动画,之后可通过 playAnimation 播放
playAnimation(key, opts?) 播放已注册的自定义动画

二、事件

全局事件开关

export function onAllEvent(): void;  // 打开所有输入事件
export function offAllEvent(): void; // 关闭所有输入事件
  • onAllEvent():确保控制器存在并打开输入监听。
  • offAllEvent():关闭输入监听,用于显示 UI 或暂停时禁止玩家输入。

默认处理包括:WASD 移动、奔跑、跳跃、鼠标视角等。

setInput 外部输入

可通过 setInput 接管输入,适用于自定义按键映射或手柄:

player.setInput({
    moveX: 1 | 0 | -1,      // 横向移动:1 右,-1 左,0 停
    moveY: 1 | 0 | -1,      // 纵向移动:1 前,-1 后,0 停
    lookDeltaX: number,      // 视角横向偏移量
    lookDeltaY: number,      // 视角纵向偏移量
    jump: boolean,           // 跳跃
    shift: boolean,          // 奔跑
    toggleView: boolean,     // 切换第一/第三人称
    toggleFly: boolean,      // 切换飞行模式
    toggleVehicle: boolean,  // 上车/下车
});

三、配置与参数说明

人物类型定义

type PlayerControllerOptions = {
    scene: THREE.Scene;
    camera: THREE.PerspectiveCamera;
    controls: OrbitControls;
    playerModel: {
        url: string;
        scale: number;
        idleAnim: string;
        walkAnim: string;
        runAnim: string;
        jumpAnim: string;
        leftWalkAnim?: string;
        rightWalkAnim?: string;
        backwardAnim?: string;
        flyAnim?: string;
        flyIdleAnim?: string;
        enterCarAnim?: string;
        exitCarAnim?: string;
        gravity?: number;
        jumpHeight?: number;
        speed?: number;
        flySpeed?: number;
        rotateY?: number;
        headObjName?: string;
        flyEnabled?: boolean;
        capsuleRadiusRatio?: number;
    };
    initPos?: THREE.Vector3;
    mouseSensity?: number;
    minCamDistance?: number;
    maxCamDistance?: number;
    colliderMeshUrl?: string;
    isShowMobileControls?: boolean;
    thirdMouseMode?: 0 | 1 | 2 | 3;
    enableZoom?: boolean;
    enableOverShoulderView?: boolean;
};

关键字段说明

字段 类型 必填 默认 说明
scene THREE.Scene three.js 场景
camera THREE.PerspectiveCamera three.js 相机
controls OrbitControls 外部相机控制器
playerModel.url string 人物模型路径(GLB/GLTF)
playerModel.scale number 人物模型缩放
playerModel.idleAnim string Idle 动画名,需与模型内动画名一致
playerModel.walkAnim string Walk 动画名,需与模型内动画名一致
playerModel.runAnim string Run 动画名,需与模型内动画名一致
playerModel.jumpAnim string Jump 动画名,需与模型内动画名一致
playerModel.leftWalkAnim string walkAnim 左走动画名,不填则复用 walkAnim
playerModel.rightWalkAnim string walkAnim 右走动画名,不填则复用 walkAnim
playerModel.backwardAnim string walkAnim 后退动画名,不填则复用 walkAnim
playerModel.flyAnim string idleAnim 飞行动画名,不填则复用 idleAnim
playerModel.flyIdleAnim string idleAnim 飞行待机动画名,不填则复用 idleAnim
playerModel.enterCarAnim string 上车动画名(使用车辆功能时必填)
playerModel.exitCarAnim string 下车动画名(使用车辆功能时必填)
playerModel.rotateY number 0 模型绕 Y 轴的额外旋转偏移
playerModel.headObjName string 头部节点名称,用于第一人称相机绑定
playerModel.speed number 300 移动速度基准值
playerModel.gravity number -2400 重力加速度基准值
playerModel.jumpHeight number 600 跳跃高度基准值
playerModel.flySpeed number 2100 飞行速度基准值
playerModel.flyEnabled boolean true 是否允许飞行模式
initPos THREE.Vector3 (0,0,0) 初始位置
mouseSensity number 5 鼠标灵敏度
minCamDistance number 100 第三人称最小相机距离
maxCamDistance number 440 第三人称最大相机距离
colliderMeshUrl string 自定义碰撞体模型路径,默认使用场景中所有网格
isShowMobileControls boolean true 移动端是否自动显示虚拟摇杆
thirdMouseMode 0|1|2|3 1 第三人称鼠标模式(见下表)
enableZoom boolean false 第三人称是否允许滚轮缩放
enableOverShoulderView boolean false 是否开启过肩视角偏移

thirdMouseMode 说明:

行为
0 隐藏鼠标,同时控制朝向和视角
1 隐藏鼠标,仅控制视角(默认)
2 显示鼠标,拖拽控制朝向和视角
3 显示鼠标,拖拽仅控制视角

车辆类型定义

type VehicleOptions = {
    url: string;
    position: THREE.Vector3;
    wheelsNames: string[];
    scale?: number;
    animations: {
        openDoorAnim?: string;
    };
    boardingPoint: THREE.Vector3;
    seatOffset?: THREE.Vector3;
    chassisRatio?: number;
    suspensionRestLengthRatio?: number;
    followVehicleDirection?: boolean;
    speedMultiplier?: number;
};

关键字段说明

字段 类型 必填 默认 说明
url string 车辆模型路径(GLB/GLTF)
position THREE.Vector3 车辆初始世界坐标
wheelsNames string[] 车轮节点名称数组,顺序为前左、前右、后左、后右
scale number 1 模型缩放系数
animations.openDoorAnim string 车门打开动画片段名称
boardingPoint THREE.Vector3 上车点局部坐标(相对车辆模型坐标系)
seatOffset THREE.Vector3 (0,0,0) 座位偏移,用于微调人物坐入后的位置
chassisRatio number 0.2 底盘高度相对于轮胎直径的比例(影响模型位置)
suspensionRestLengthRatio number 0.2 悬架弹簧静止长度相对于轮胎直径的比例(影响碰撞底盘高度)
followVehicleDirection boolean true 行驶时相机是否跟随车辆速度方向自动转向车辆正后方
speedMultiplier number 1 车辆速度倍率,用于调节不同车辆的速度差异


四、自定义动画

除内置动画外,可在加载模型后注册并播放自定义动画片段。

registerAnimation

player.registerAnimation(key, clipName, opts?)
参数 类型 说明
key string 动画标识符,之后通过此 key 播放
clipName string GLB 模型内动画片段的原始名称
opts.loop boolean 是否循环,默认 true
opts.timeScale number 播放速度倍率,默认 1
opts.duration number 指定播放时长(秒),会自动计算 timeScale,与 timeScale 二选一
opts.clampWhenFinished boolean 单次播放结束后是否停在最后一帧,默认 false
opts.onFinished () => void 动画播放完毕回调(仅单次播放时有效)

playAnimation

player.playAnimation(key, opts?)
参数 类型 说明
key string 已注册的动画标识符
opts.fade number 过渡时间(秒),默认 0.18
opts.force boolean 是否强制从头重播(即使当前已在播放该动画)

示例:

// 注册一个单次播放、播完回调的动画
player.registerAnimation("attack", "Attack_Clip", {
    loop: false,
    duration: 1.2,
    clampWhenFinished: true,
    onFinished: () => console.log("攻击结束"),
});

// 播放
player.playAnimation("attack", { force: true });

感谢

three-mesh-bvh

three