three-player-controller 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -1
- package/dist/index.d.mts +27 -30
- package/dist/index.d.ts +27 -30
- package/dist/index.js +11 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -22,7 +22,6 @@ npm install three-player-controller
|
|
|
22
22
|
|
|
23
23
|

|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
# 使用
|
|
27
26
|
|
|
28
27
|
```js
|
|
@@ -51,6 +50,107 @@ player.init({
|
|
|
51
50
|
player.update();
|
|
52
51
|
```
|
|
53
52
|
|
|
53
|
+
# API
|
|
54
|
+
|
|
55
|
+
## 一、初始化
|
|
56
|
+
|
|
57
|
+
### 导出函数
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
export function playerController(): {
|
|
61
|
+
init: (opts: PlayerControllerOptions, callback?: () => void) => void;
|
|
62
|
+
changeView: () => void;
|
|
63
|
+
reset: (pos?: THREE.Vector3) => void;
|
|
64
|
+
update: (dt?: number) => void;
|
|
65
|
+
destroy: () => void;
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 方法说明
|
|
70
|
+
|
|
71
|
+
- `init(opts, callback?)`
|
|
72
|
+
初始化控制器。`callback` 在资源加载完成后调用。
|
|
73
|
+
- `update(dt?)`
|
|
74
|
+
每帧调用。
|
|
75
|
+
- `changeView()`
|
|
76
|
+
在第一/第三人称间切换。
|
|
77
|
+
- `reset(pos?)`
|
|
78
|
+
复位玩家到指定位置。
|
|
79
|
+
- `destroy()`
|
|
80
|
+
销毁控制器。
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 二、事件
|
|
85
|
+
|
|
86
|
+
### 全局事件开关
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
export function onAllEvent(): void; // 打开所有输入事件
|
|
90
|
+
export function offAllEvent(): void; // 关闭所有输入事件
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 说明
|
|
94
|
+
|
|
95
|
+
- `onAllEvent()`:确保控制器存在并打开输入监听。
|
|
96
|
+
- `offAllEvent()`:关闭输入监听(用于显示 UI 或暂停时禁止玩家输入)。
|
|
97
|
+
- 默认处理包括:WASD 移动、奔跑、跳跃、鼠标视角等。
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 三、配置与参数说明
|
|
102
|
+
|
|
103
|
+
### 类型定义
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
type PlayerControllerOptions = {
|
|
107
|
+
scene: THREE.Scene;
|
|
108
|
+
camera: THREE.PerspectiveCamera;
|
|
109
|
+
controls: OrbitControls;
|
|
110
|
+
playerModel: {
|
|
111
|
+
url: string;
|
|
112
|
+
idleAnim: string;
|
|
113
|
+
walkAnim: string;
|
|
114
|
+
runAnim: string;
|
|
115
|
+
jumpAnim: string;
|
|
116
|
+
leftWalkAnim?: string;
|
|
117
|
+
rightWalkAnim?: string;
|
|
118
|
+
backwardAnim?: string;
|
|
119
|
+
flyAnim?: string;
|
|
120
|
+
flyIdleAnim?: string;
|
|
121
|
+
scale: number;
|
|
122
|
+
gravity?: number;
|
|
123
|
+
jumpHeight?: number;
|
|
124
|
+
speed?: number;
|
|
125
|
+
};
|
|
126
|
+
initPos?: THREE.Vector3;
|
|
127
|
+
mouseSensity?: number;
|
|
128
|
+
minCamDistance?: number;
|
|
129
|
+
maxCamDistance?: number;
|
|
130
|
+
colliderMeshUrl?: string;
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 关键字段说明
|
|
135
|
+
|
|
136
|
+
| 字段 | 类型 | 默认 / 说明 |
|
|
137
|
+
| ------------------------------------------------------------ | ------------------------: | ---------------------------------------------- |
|
|
138
|
+
| `scene` | `THREE.Scene` | three.js 场景(必填) |
|
|
139
|
+
| `camera` | `THREE.PerspectiveCamera` | three.js 相机(必填) |
|
|
140
|
+
| `controls` | `OrbitControls` | 外部相机控制器(必填) |
|
|
141
|
+
| `playerModel.url` | `string` | 人物模型路径(GLB/GLTF,必填) |
|
|
142
|
+
| `playerModel.scale` | `number` | 人物模型缩放(必填) |
|
|
143
|
+
| `playerModel.idleAnim` / `walkAnim` / `runAnim` / `jumpAnim` | `string` | 人物动画名,需与人物模型中动画名称一致(必填) |
|
|
144
|
+
| `playerModel.speed` | `number` | 基准速度,默认约 `4.0` |
|
|
145
|
+
| `playerModel.gravity` | `number` | 重力加速度,默认 `9.8` |
|
|
146
|
+
| `playerModel.jumpHeight` | `number` | 跳跃高度 |
|
|
147
|
+
| `initPos` | `THREE.Vector3` | 初始位置,默认 `(0,0,0)` |
|
|
148
|
+
| `mouseSensity` | `number` | 鼠标灵敏度 |
|
|
149
|
+
| `minCamDistance` / `maxCamDistance` | `number` | 第三人称相机距离限制 |
|
|
150
|
+
| `colliderMeshUrl` | `string?` | 自制碰撞体模型路径 |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
54
154
|
# 感谢
|
|
55
155
|
|
|
56
156
|
[three-mesh-bvh](https://github.com/gkjohnson/three-mesh-bvh)
|
package/dist/index.d.mts
CHANGED
|
@@ -1,41 +1,38 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
3
3
|
|
|
4
|
+
type PlayerControllerOptions = {
|
|
5
|
+
scene: THREE.Scene;
|
|
6
|
+
camera: THREE.PerspectiveCamera;
|
|
7
|
+
controls: OrbitControls;
|
|
8
|
+
playerModel: {
|
|
9
|
+
url: string;
|
|
10
|
+
idleAnim: string;
|
|
11
|
+
walkAnim: string;
|
|
12
|
+
runAnim: string;
|
|
13
|
+
jumpAnim: string;
|
|
14
|
+
leftWalkAnim?: string;
|
|
15
|
+
rightWalkAnim?: string;
|
|
16
|
+
backwardAnim?: string;
|
|
17
|
+
flyAnim?: string;
|
|
18
|
+
flyIdleAnim?: string;
|
|
19
|
+
scale: number;
|
|
20
|
+
gravity?: number;
|
|
21
|
+
jumpHeight?: number;
|
|
22
|
+
speed?: number;
|
|
23
|
+
};
|
|
24
|
+
initPos?: THREE.Vector3;
|
|
25
|
+
mouseSensity?: number;
|
|
26
|
+
minCamDistance?: number;
|
|
27
|
+
maxCamDistance?: number;
|
|
28
|
+
colliderMeshUrl?: string;
|
|
29
|
+
};
|
|
4
30
|
declare function playerController(): {
|
|
5
|
-
init: (opts:
|
|
6
|
-
scene: THREE.Scene;
|
|
7
|
-
camera: THREE.PerspectiveCamera;
|
|
8
|
-
controls: OrbitControls;
|
|
9
|
-
playerModel: {
|
|
10
|
-
url: string;
|
|
11
|
-
idleAnim: string;
|
|
12
|
-
walkAnim: string;
|
|
13
|
-
runAnim: string;
|
|
14
|
-
jumpAnim: string;
|
|
15
|
-
leftWalkAnim?: string;
|
|
16
|
-
rightWalkAnim?: string;
|
|
17
|
-
backwardAnim?: string;
|
|
18
|
-
flyAnim?: string;
|
|
19
|
-
flyIdleAnim?: string;
|
|
20
|
-
scale: number;
|
|
21
|
-
gravity?: number;
|
|
22
|
-
jumpHeight?: number;
|
|
23
|
-
speed?: number;
|
|
24
|
-
};
|
|
25
|
-
initPos?: THREE.Vector3;
|
|
26
|
-
mouseSensity?: number;
|
|
27
|
-
minCamDistance?: number;
|
|
28
|
-
maxCamDistance?: number;
|
|
29
|
-
}, callback?: () => void) => Promise<void>;
|
|
31
|
+
init: (opts: PlayerControllerOptions, callback?: () => void) => Promise<void>;
|
|
30
32
|
changeView: () => void;
|
|
31
|
-
createBVH: (url?: string) => Promise<void>;
|
|
32
|
-
createPlayer: () => void;
|
|
33
33
|
reset: (pos?: THREE.Vector3) => void;
|
|
34
34
|
update: (dt?: number) => Promise<void>;
|
|
35
35
|
destroy: () => void;
|
|
36
|
-
displayCollider: boolean;
|
|
37
|
-
displayPlayer: boolean;
|
|
38
|
-
displayVisualizer: boolean;
|
|
39
36
|
};
|
|
40
37
|
declare function onAllEvent(): void;
|
|
41
38
|
declare function offAllEvent(): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,41 +1,38 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
3
3
|
|
|
4
|
+
type PlayerControllerOptions = {
|
|
5
|
+
scene: THREE.Scene;
|
|
6
|
+
camera: THREE.PerspectiveCamera;
|
|
7
|
+
controls: OrbitControls;
|
|
8
|
+
playerModel: {
|
|
9
|
+
url: string;
|
|
10
|
+
idleAnim: string;
|
|
11
|
+
walkAnim: string;
|
|
12
|
+
runAnim: string;
|
|
13
|
+
jumpAnim: string;
|
|
14
|
+
leftWalkAnim?: string;
|
|
15
|
+
rightWalkAnim?: string;
|
|
16
|
+
backwardAnim?: string;
|
|
17
|
+
flyAnim?: string;
|
|
18
|
+
flyIdleAnim?: string;
|
|
19
|
+
scale: number;
|
|
20
|
+
gravity?: number;
|
|
21
|
+
jumpHeight?: number;
|
|
22
|
+
speed?: number;
|
|
23
|
+
};
|
|
24
|
+
initPos?: THREE.Vector3;
|
|
25
|
+
mouseSensity?: number;
|
|
26
|
+
minCamDistance?: number;
|
|
27
|
+
maxCamDistance?: number;
|
|
28
|
+
colliderMeshUrl?: string;
|
|
29
|
+
};
|
|
4
30
|
declare function playerController(): {
|
|
5
|
-
init: (opts:
|
|
6
|
-
scene: THREE.Scene;
|
|
7
|
-
camera: THREE.PerspectiveCamera;
|
|
8
|
-
controls: OrbitControls;
|
|
9
|
-
playerModel: {
|
|
10
|
-
url: string;
|
|
11
|
-
idleAnim: string;
|
|
12
|
-
walkAnim: string;
|
|
13
|
-
runAnim: string;
|
|
14
|
-
jumpAnim: string;
|
|
15
|
-
leftWalkAnim?: string;
|
|
16
|
-
rightWalkAnim?: string;
|
|
17
|
-
backwardAnim?: string;
|
|
18
|
-
flyAnim?: string;
|
|
19
|
-
flyIdleAnim?: string;
|
|
20
|
-
scale: number;
|
|
21
|
-
gravity?: number;
|
|
22
|
-
jumpHeight?: number;
|
|
23
|
-
speed?: number;
|
|
24
|
-
};
|
|
25
|
-
initPos?: THREE.Vector3;
|
|
26
|
-
mouseSensity?: number;
|
|
27
|
-
minCamDistance?: number;
|
|
28
|
-
maxCamDistance?: number;
|
|
29
|
-
}, callback?: () => void) => Promise<void>;
|
|
31
|
+
init: (opts: PlayerControllerOptions, callback?: () => void) => Promise<void>;
|
|
30
32
|
changeView: () => void;
|
|
31
|
-
createBVH: (url?: string) => Promise<void>;
|
|
32
|
-
createPlayer: () => void;
|
|
33
33
|
reset: (pos?: THREE.Vector3) => void;
|
|
34
34
|
update: (dt?: number) => Promise<void>;
|
|
35
35
|
destroy: () => void;
|
|
36
|
-
displayCollider: boolean;
|
|
37
|
-
displayPlayer: boolean;
|
|
38
|
-
displayVisualizer: boolean;
|
|
39
36
|
};
|
|
40
37
|
declare function onAllEvent(): void;
|
|
41
38
|
declare function offAllEvent(): void;
|
package/dist/index.js
CHANGED
|
@@ -285,6 +285,7 @@ var PlayerController = class {
|
|
|
285
285
|
async init(opts, callback) {
|
|
286
286
|
this.scene = opts.scene;
|
|
287
287
|
this.camera = opts.camera;
|
|
288
|
+
this.camera.rotation.order = "YXZ";
|
|
288
289
|
this.controls = opts.controls;
|
|
289
290
|
this.playerModel = opts.playerModel;
|
|
290
291
|
this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);
|
|
@@ -293,13 +294,14 @@ var PlayerController = class {
|
|
|
293
294
|
this.visualizeDepth = 0 * s;
|
|
294
295
|
this.gravity = opts.playerModel.gravity ? opts.playerModel.gravity * s : -2400 * s;
|
|
295
296
|
this.jumpHeight = opts.playerModel.jumpHeight ? opts.playerModel.jumpHeight * s : 800 * s;
|
|
296
|
-
this.
|
|
297
|
+
this.originPlayerSpeed = opts.playerModel.speed ? opts.playerModel.speed * s : 400 * s;
|
|
298
|
+
this.playerSpeed = this.originPlayerSpeed;
|
|
297
299
|
this._camCollisionLerp = 0.18;
|
|
298
300
|
this._camEpsilon = 35 * s;
|
|
299
301
|
this._minCamDistance = opts.minCamDistance ? opts.minCamDistance * s : 100 * s;
|
|
300
302
|
this._maxCamDistance = opts.maxCamDistance ? opts.maxCamDistance * s : 440 * s;
|
|
301
303
|
this.orginMaxCamDistance = this._maxCamDistance;
|
|
302
|
-
await this.createBVH();
|
|
304
|
+
await this.createBVH(opts.colliderMeshUrl);
|
|
303
305
|
this.createPlayer();
|
|
304
306
|
await this.loadPersonGLB();
|
|
305
307
|
if (this.isFirstPerson && this.player) {
|
|
@@ -571,9 +573,9 @@ var PlayerController = class {
|
|
|
571
573
|
}
|
|
572
574
|
}
|
|
573
575
|
if (this.isFlying && this.fwdPressed) {
|
|
574
|
-
this.playerSpeed = this.shiftPressed ?
|
|
576
|
+
this.playerSpeed = this.shiftPressed ? this.originPlayerSpeed * 12 : this.originPlayerSpeed * 7;
|
|
575
577
|
} else {
|
|
576
|
-
this.playerSpeed = this.shiftPressed ?
|
|
578
|
+
this.playerSpeed = this.shiftPressed ? this.originPlayerSpeed * 2 : this.originPlayerSpeed;
|
|
577
579
|
}
|
|
578
580
|
this.moveDir.normalize().applyAxisAngle(this.upVector, angle);
|
|
579
581
|
this.player.position.addScaledVector(
|
|
@@ -762,7 +764,7 @@ var PlayerController = class {
|
|
|
762
764
|
}
|
|
763
765
|
}
|
|
764
766
|
}
|
|
765
|
-
// 重置
|
|
767
|
+
// 重置
|
|
766
768
|
reset(position) {
|
|
767
769
|
if (!this.player) return;
|
|
768
770
|
this.playerVelocity.set(0, 0, 0);
|
|
@@ -907,7 +909,9 @@ var PlayerController = class {
|
|
|
907
909
|
console.error("\u5408\u5E76\u51E0\u4F55\u5931\u8D25");
|
|
908
910
|
return;
|
|
909
911
|
}
|
|
910
|
-
merged.boundsTree = new import_three_mesh_bvh.MeshBVH(merged
|
|
912
|
+
merged.boundsTree = new import_three_mesh_bvh.MeshBVH(merged, {
|
|
913
|
+
maxDepth: 100
|
|
914
|
+
});
|
|
911
915
|
this.collider = new THREE.Mesh(
|
|
912
916
|
merged,
|
|
913
917
|
new THREE.MeshBasicMaterial({
|
|
@@ -923,7 +927,6 @@ var PlayerController = class {
|
|
|
923
927
|
this.scene.add(this.visualizer);
|
|
924
928
|
}
|
|
925
929
|
this.boundingBoxMinY = this.collider.geometry.boundingBox.min.y;
|
|
926
|
-
console.log("bvh\u52A0\u8F7D\u6A21\u578B\u6210\u529F", this.collider);
|
|
927
930
|
}
|
|
928
931
|
};
|
|
929
932
|
function playerController() {
|
|
@@ -932,14 +935,9 @@ function playerController() {
|
|
|
932
935
|
return {
|
|
933
936
|
init: (opts, callback) => c.init(opts, callback),
|
|
934
937
|
changeView: () => c.changeView(),
|
|
935
|
-
createBVH: (url = "") => c.createBVH(url),
|
|
936
|
-
createPlayer: () => c.createPlayer(),
|
|
937
938
|
reset: (pos) => c.reset(pos),
|
|
938
939
|
update: (dt) => c.update(dt),
|
|
939
|
-
destroy: () => c.destroy()
|
|
940
|
-
displayCollider: c.displayCollider,
|
|
941
|
-
displayPlayer: c.displayPlayer,
|
|
942
|
-
displayVisualizer: c.displayVisualizer
|
|
940
|
+
destroy: () => c.destroy()
|
|
943
941
|
};
|
|
944
942
|
}
|
|
945
943
|
function onAllEvent() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/playerController.ts"],"sourcesContent":["export * from \"./playerController\";\r\n","import * as THREE from \"three\";\r\nimport { acceleratedRaycast, MeshBVH, MeshBVHHelper } from \"three-mesh-bvh\";\r\nimport type { GLTF } from \"three/examples/jsm/Addons.js\";\r\nimport { OrbitControls } from \"three/examples/jsm/controls/OrbitControls.js\";\r\nimport { RoundedBoxGeometry } from \"three/examples/jsm/geometries/RoundedBoxGeometry.js\";\r\nimport { DRACOLoader } from \"three/examples/jsm/loaders/DRACOLoader.js\";\r\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\r\nimport * as BufferGeometryUtils from \"three/examples/jsm/utils/BufferGeometryUtils.js\";\r\n\r\nTHREE.Mesh.prototype.raycast = acceleratedRaycast;\r\n\r\nlet controllerInstance: PlayerController | null = null; // 单例实例\r\nconst clock = new THREE.Clock();\r\n\r\nclass PlayerController {\r\n loader: GLTFLoader = new GLTFLoader();\r\n\r\n // 基本配置与参数\r\n scene!: THREE.Scene;\r\n camera!: THREE.PerspectiveCamera;\r\n controls!: OrbitControls;\r\n initPos!: THREE.Vector3;\r\n playerModel!: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n visualizeDepth!: number;\r\n gravity!: number;\r\n jumpHeight!: number;\r\n playerSpeed!: number;\r\n mouseSensity!: number;\r\n\r\n playerRadius: number = 45;\r\n playerHeight: number = 180;\r\n isFirstPerson: boolean = false;\r\n boundingBoxMinY: number = 0;\r\n // 测试参数\r\n displayPlayer: boolean = false;\r\n displayCollider: boolean = false;\r\n displayVisualizer: boolean = false;\r\n\r\n // 场景对象\r\n collider: THREE.Mesh | null = null;\r\n visualizer: MeshBVHHelper | null = null;\r\n player!: THREE.Mesh & { capsuleInfo?: any };\r\n person: THREE.Object3D | null = null;\r\n\r\n // 状态开关\r\n playerIsOnGround: boolean = false;\r\n isupdate: boolean = true;\r\n isFlying: boolean = false;\r\n\r\n // 输入状态\r\n fwdPressed: boolean = false;\r\n bkdPressed: boolean = false;\r\n lftPressed: boolean = false;\r\n rgtPressed: boolean = false;\r\n spacePressed: boolean = false;\r\n ctPressed: boolean = false;\r\n shiftPressed: boolean = false;\r\n\r\n // 第三人称\r\n _camCollisionLerp: number = 0.18; // 平滑系数\r\n _camEpsilon: number = 0.35; // 摄像机与障碍物之间的安全距离(米)\r\n _minCamDistance: number = 1.0; // 摄像机最小距离\r\n _maxCamDistance: number = 4.4; // 摄像机最大距离\r\n orginMaxCamDistance: number = 4.4;\r\n\r\n // 物理/运动\r\n playerVelocity = new THREE.Vector3(); // 玩家速度向量\r\n readonly upVector = new THREE.Vector3(0, 1, 0);\r\n\r\n // 临时复用向量/矩阵\r\n readonly tempVector = new THREE.Vector3();\r\n readonly tempVector2 = new THREE.Vector3();\r\n readonly tempBox = new THREE.Box3();\r\n readonly tempMat = new THREE.Matrix4();\r\n readonly tempSegment = new THREE.Line3();\r\n\r\n // 动画相关\r\n personMixer?: THREE.AnimationMixer;\r\n personActions?: Map<string, THREE.AnimationAction>;\r\n idleAction!: THREE.AnimationAction;\r\n walkAction!: THREE.AnimationAction;\r\n leftWalkAction!: THREE.AnimationAction;\r\n rightWalkAction!: THREE.AnimationAction;\r\n backwardAction!: THREE.AnimationAction;\r\n jumpAction!: THREE.AnimationAction;\r\n runAction!: THREE.AnimationAction;\r\n flyidleAction!: THREE.AnimationAction;\r\n flyAction!: THREE.AnimationAction;\r\n controlDroneAction!: THREE.AnimationAction;\r\n actionState!: THREE.AnimationAction;\r\n\r\n // 复用向量:用于相机朝向 / 移动\r\n readonly camDir = new THREE.Vector3();\r\n readonly moveDir = new THREE.Vector3();\r\n readonly targetQuat = new THREE.Quaternion();\r\n readonly targetMat = new THREE.Matrix4();\r\n readonly rotationSpeed = 10;\r\n readonly DIR_FWD = new THREE.Vector3(0, 0, -1);\r\n readonly DIR_BKD = new THREE.Vector3(0, 0, 1);\r\n readonly DIR_LFT = new THREE.Vector3(-1, 0, 0);\r\n readonly DIR_RGT = new THREE.Vector3(1, 0, 0);\r\n readonly DIR_UP = new THREE.Vector3(0, 1, 0);\r\n\r\n readonly _personToCam = new THREE.Vector3();\r\n\r\n readonly _originTmp = new THREE.Vector3();\r\n readonly _raycaster = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -1, 0)\r\n );\r\n readonly _raycasterPersonToCam = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3()\r\n );\r\n\r\n // 射线检测时只返回第一个碰撞\r\n constructor() {\r\n (this._raycaster as any).firstHitOnly = true;\r\n (this._raycasterPersonToCam as any).firstHitOnly = true;\r\n }\r\n\r\n // 初始化\r\n async init(\r\n opts: {\r\n scene: THREE.Scene;\r\n camera: THREE.PerspectiveCamera;\r\n controls: OrbitControls;\r\n playerModel: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n initPos?: THREE.Vector3;\r\n mouseSensity?: number;\r\n minCamDistance?: number;\r\n maxCamDistance?: number;\r\n },\r\n callback?: () => void\r\n ) {\r\n this.scene = opts.scene;\r\n this.camera = opts.camera;\r\n this.controls = opts.controls;\r\n this.playerModel = opts.playerModel;\r\n this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);\r\n this.mouseSensity = opts.mouseSensity ? opts.mouseSensity : 5;\r\n\r\n const s = this.playerModel.scale;\r\n this.visualizeDepth = 0 * s;\r\n this.gravity = opts.playerModel.gravity\r\n ? opts.playerModel.gravity * s\r\n : -2400 * s;\r\n this.jumpHeight = opts.playerModel.jumpHeight\r\n ? opts.playerModel.jumpHeight * s\r\n : 800 * s;\r\n this.playerSpeed = opts.playerModel.speed\r\n ? opts.playerModel.speed * s\r\n : 400 * s;\r\n\r\n this._camCollisionLerp = 0.18;\r\n this._camEpsilon = 35 * s;\r\n this._minCamDistance = opts.minCamDistance\r\n ? opts.minCamDistance * s\r\n : 100 * s;\r\n this._maxCamDistance = opts.maxCamDistance\r\n ? opts.maxCamDistance * s\r\n : 440 * s;\r\n this.orginMaxCamDistance = this._maxCamDistance;\r\n\r\n // 创建bvh\r\n await this.createBVH();\r\n\r\n // 创建玩家\r\n this.createPlayer();\r\n\r\n // 加载玩家模型\r\n await this.loadPersonGLB();\r\n\r\n // 等待资源加载完毕再设置摄像机\r\n if (this.isFirstPerson && this.player) {\r\n this.player.add(this.camera);\r\n }\r\n this.onAllEvent(); // 绑定事件\r\n this.setCameraPos();\r\n this.setControls();\r\n if (callback) callback();\r\n }\r\n\r\n // 第一/三视角切换\r\n changeView() {\r\n this.isFirstPerson = !this.isFirstPerson;\r\n if (this.isFirstPerson) {\r\n this.player.attach(this.camera);\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n this.camera.rotation.set(0, Math.PI, 0);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n } else {\r\n this.scene.attach(this.camera);\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n this.controls.target.copy(worldPos);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n }\r\n }\r\n\r\n // 摄像机/控制器设置\r\n setCameraPos() {\r\n if (this.isFirstPerson) {\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n } else {\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n }\r\n this.camera.updateProjectionMatrix();\r\n }\r\n\r\n // 设置控制器\r\n setControls() {\r\n this.controls.enabled = false;\r\n this.controls.maxPolarAngle = Math.PI * (300 / 360);\r\n }\r\n\r\n // 重置控制器\r\n resetControls() {\r\n if (!this.controls) return;\r\n this.controls.enabled = true;\r\n this.controls.enablePan = true;\r\n this.controls.maxPolarAngle = Math.PI / 2;\r\n this.controls.rotateSpeed = 1;\r\n this.controls.enableZoom = true;\r\n this.controls.mouseButtons = {\r\n LEFT: 0,\r\n MIDDLE: 1,\r\n RIGHT: 2,\r\n };\r\n }\r\n\r\n // 初始化加载器\r\n async initLoader() {\r\n const dracoLoader = new DRACOLoader();\r\n dracoLoader.setDecoderPath(\r\n \"https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/\"\r\n );\r\n dracoLoader.setDecoderConfig({ type: \"js\" });\r\n this.loader.setDRACOLoader(dracoLoader);\r\n }\r\n\r\n // 人物与动画加载\r\n async loadPersonGLB() {\r\n try {\r\n const gltf: GLTF = await this.loader.loadAsync(this.playerModel.url);\r\n this.person = gltf.scene;\r\n const sc = this.playerModel.scale;\r\n const h = this.playerHeight * sc;\r\n this.person.scale.set(sc, sc, sc);\r\n this.person.position.set(0, -h * 0.75, 0);\r\n this.person.traverse((child: any) => {\r\n if (child.isMesh) {\r\n child.castShadow = true;\r\n\r\n const mat = child.material;\r\n if (!mat) return;\r\n\r\n // 处理多材质的情况\r\n const mats = Array.isArray(mat) ? mat : [mat];\r\n\r\n mats.forEach((m) => {\r\n // console.log(\"mat\", m);\r\n // m.envMap = this.scene.background;\r\n // m.envMapIntensity = 1.0;\r\n });\r\n }\r\n });\r\n this.player.add(this.person);\r\n this.reset();\r\n\r\n // 创建人物 mixer 与 actions\r\n this.personMixer = new THREE.AnimationMixer(this.person);\r\n const animations = gltf.animations ?? [];\r\n this.personActions = new Map<string, THREE.AnimationAction>();\r\n // 取出动作并注册到 map\r\n const findClip = (name: string) =>\r\n animations.find((a: any) => a.name === name);\r\n const regs: [string, string][] = [\r\n [this.playerModel.idleAnim, \"idle\"],\r\n [this.playerModel.walkAnim, \"walking\"],\r\n [\r\n this.playerModel.leftWalkAnim || this.playerModel.walkAnim,\r\n \"left_walking\",\r\n ],\r\n [\r\n this.playerModel.rightWalkAnim || this.playerModel.walkAnim,\r\n \"right_walking\",\r\n ],\r\n [\r\n this.playerModel.backwardAnim || this.playerModel.walkAnim,\r\n \"walking_backward\",\r\n ],\r\n [this.playerModel.jumpAnim, \"jumping\"],\r\n [this.playerModel.runAnim, \"running\"],\r\n [this.playerModel.flyIdleAnim || this.playerModel.idleAnim, \"flyidle\"],\r\n [this.playerModel.flyAnim || this.playerModel.idleAnim, \"flying\"],\r\n ];\r\n\r\n // 注册动作并设置循环模式\r\n for (const [key, clipName] of regs) {\r\n const clip = findClip(key);\r\n if (!clip) continue;\r\n const action = this.personMixer.clipAction(clip);\r\n\r\n if (clipName === \"jumping\") {\r\n action.setLoop(THREE.LoopOnce, 1); // 播放一次\r\n action.clampWhenFinished = true;\r\n action.setEffectiveTimeScale(1.2); // 播放速度\r\n } else {\r\n action.setLoop(THREE.LoopRepeat, Infinity); // 循环播放\r\n action.clampWhenFinished = false;\r\n action.setEffectiveTimeScale(1);\r\n }\r\n\r\n action.enabled = true; // 激活\r\n action.setEffectiveWeight(0); // 初始权重为0\r\n this.personActions.set(clipName, action);\r\n }\r\n\r\n // 把actions激活\r\n this.idleAction = this.personActions.get(\"idle\")!;\r\n this.walkAction = this.personActions.get(\"walking\")!;\r\n this.leftWalkAction = this.personActions.get(\"left_walking\")!;\r\n this.rightWalkAction = this.personActions.get(\"right_walking\")!;\r\n this.backwardAction = this.personActions.get(\"walking_backward\")!;\r\n this.jumpAction = this.personActions.get(\"jumping\")!;\r\n this.runAction = this.personActions.get(\"running\")!;\r\n this.flyidleAction = this.personActions.get(\"flyidle\")!;\r\n this.flyAction = this.personActions.get(\"flying\")!;\r\n\r\n // 激活空闲动作\r\n this.idleAction.setEffectiveWeight(1);\r\n this.idleAction.play();\r\n this.actionState = this.idleAction;\r\n\r\n this.personMixer.addEventListener(\"finished\", (ev: any) => {\r\n const finishedAction: THREE.AnimationAction = ev.action;\r\n\r\n if (finishedAction === this.jumpAction) {\r\n // jump 播放结束后的逻辑\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) this.playPersonAnimationByName(\"running\");\r\n else this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n if (this.rgtPressed || this.lftPressed) {\r\n this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"idle\");\r\n }\r\n });\r\n } catch (error) {}\r\n }\r\n\r\n // 平滑切换人物动画\r\n playPersonAnimationByName(name: string, fade = 0.18) {\r\n // console.log(\"播放动画\", name);\r\n if (!this.personActions) return;\r\n if (this.ctPressed) return;\r\n\r\n const next = this.personActions.get(name);\r\n if (!next) return;\r\n\r\n // 如果是同一个action,直接返回\r\n if (this.actionState === next) return;\r\n\r\n const prev = this.actionState;\r\n\r\n // 对于一次性动作先reset()\r\n next.reset();\r\n next.setEffectiveWeight(1);\r\n next.play();\r\n\r\n if (prev && prev !== next) {\r\n // 让 prev 淡出,next 淡入\r\n prev.fadeOut(fade);\r\n next.fadeIn(fade);\r\n } else {\r\n // 时直接淡入\r\n next.fadeIn(fade);\r\n }\r\n\r\n this.actionState = next;\r\n }\r\n\r\n // 创建玩家胶囊体\r\n createPlayer() {\r\n const material = new THREE.MeshStandardMaterial({\r\n color: new THREE.Color(1, 0, 0),\r\n shadowSide: THREE.DoubleSide,\r\n depthTest: false,\r\n });\r\n material.transparent = true;\r\n material.opacity = this.displayPlayer ? 0.5 : 0;\r\n material.wireframe = true;\r\n material.depthWrite = false;\r\n\r\n const r = this.playerRadius * this.playerModel.scale;\r\n const h = this.playerHeight * this.playerModel.scale;\r\n this.player = new THREE.Mesh(\r\n new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75),\r\n material\r\n ) as typeof this.player;\r\n\r\n this.player.geometry.translate(0, -h * 0.25, 0);\r\n this.player.capsuleInfo = {\r\n radius: r,\r\n segment: new THREE.Line3(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -h * 0.5, 0)\r\n ),\r\n };\r\n\r\n this.player.name = \"capsule\";\r\n this.scene.add(this.player);\r\n this.reset();\r\n }\r\n\r\n // 获取法线与Y轴的夹角\r\n getAngleWithYAxis(normal: { x: number; y: number; z: number }): number {\r\n // Y轴正方向向量\r\n const yAxis = { x: 0, y: 1, z: 0 };\r\n\r\n // 向量点积\r\n const dotProduct =\r\n normal.x * yAxis.x + normal.y * yAxis.y + normal.z * yAxis.z;\r\n\r\n // 向量模长\r\n const normalMagnitude = Math.sqrt(\r\n normal.x * normal.x + normal.y * normal.y + normal.z * normal.z\r\n );\r\n const yAxisMagnitude = 1; // Y轴单位向量长度为1\r\n\r\n // 计算夹角余弦值\r\n const cosTheta = dotProduct / (normalMagnitude * yAxisMagnitude);\r\n\r\n // 返回夹角(弧度)\r\n return Math.acos(cosTheta);\r\n }\r\n\r\n // 每帧更新\r\n async update(delta: number = clock.getDelta()) {\r\n if (!this.isupdate || !this.player || !this.collider) return;\r\n delta = Math.min(delta, 1 / 30);\r\n\r\n if (!this.isFlying)\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.updateMixers(delta);\r\n this.camera.getWorldDirection(this.camDir);\r\n let angle = Math.atan2(this.camDir.z, this.camDir.x) + Math.PI / 2;\r\n angle = 2 * Math.PI - angle;\r\n\r\n this.moveDir.set(0, 0, 0);\r\n if (this.fwdPressed) this.moveDir.add(this.DIR_FWD);\r\n if (this.bkdPressed) this.moveDir.add(this.DIR_BKD);\r\n if (this.lftPressed) this.moveDir.add(this.DIR_LFT);\r\n if (this.rgtPressed) this.moveDir.add(this.DIR_RGT);\r\n if (this.isFlying) {\r\n if (this.fwdPressed) {\r\n this.moveDir.y = this.camDir.y;\r\n } else {\r\n this.moveDir.y = 0;\r\n }\r\n if (this.spacePressed) {\r\n this.moveDir.add(this.DIR_UP);\r\n }\r\n }\r\n // 设置速度\r\n if (this.isFlying && this.fwdPressed) {\r\n this.playerSpeed = this.shiftPressed\r\n ? 4000 * this.playerModel.scale\r\n : 3000 * this.playerModel.scale;\r\n } else {\r\n this.playerSpeed = this.shiftPressed\r\n ? 900 * this.playerModel.scale\r\n : 400 * this.playerModel.scale;\r\n }\r\n this.moveDir.normalize().applyAxisAngle(this.upVector, angle);\r\n this.player.position.addScaledVector(\r\n this.moveDir,\r\n this.playerSpeed * delta\r\n );\r\n\r\n // 向下射线检测地面高度\r\n let playerDistanceFromGround = Infinity;\r\n this._originTmp.set(\r\n this.player.position.x,\r\n this.player.position.y,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n playerDistanceFromGround = this.player.position.y - intersects[0].point.y;\r\n const normal = intersects[0].normal as THREE.Vector3;\r\n const angle = (this.getAngleWithYAxis(normal) * 180) / Math.PI;\r\n const maxH = this.playerHeight * this.playerModel.scale * 0.9; // 坡度高度阈值\r\n const h = this.playerHeight * this.playerModel.scale * 0.75; // 正常高度\r\n const minH = this.playerHeight * this.playerModel.scale * 0.7; // 最小高度\r\n\r\n if (this.isFlying) {\r\n } else {\r\n if (playerDistanceFromGround > maxH) {\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = false;\r\n } else if (\r\n playerDistanceFromGround > h &&\r\n playerDistanceFromGround < maxH\r\n ) {\r\n if (angle >= 0 && angle < 5) {\r\n // 平地\r\n // this.player.position.y = intersects[0].point.y + h;\r\n // this.playerVelocity.set(0, 0, 0);\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = true;\r\n } else {\r\n // 坡地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n }\r\n } else if (\r\n playerDistanceFromGround > minH &&\r\n playerDistanceFromGround < h\r\n ) {\r\n // 误差范围内 在平地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n } else if (playerDistanceFromGround < minH) {\r\n // 强行拉回\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.set(\r\n this.player.position.x,\r\n intersects[0].point.y + h,\r\n this.player.position.z\r\n );\r\n this.playerIsOnGround = true;\r\n }\r\n }\r\n }\r\n\r\n // 更新玩家矩阵\r\n this.player.updateMatrixWorld();\r\n // 碰撞检测\r\n const capsuleInfo = this.player.capsuleInfo;\r\n this.tempBox.makeEmpty();\r\n this.tempMat.copy(this.collider!.matrixWorld).invert();\r\n this.tempSegment.copy(capsuleInfo.segment);\r\n this.tempSegment.start\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n this.tempSegment.end\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n\r\n this.tempBox.expandByPoint(this.tempSegment.start);\r\n this.tempBox.expandByPoint(this.tempSegment.end);\r\n this.tempBox.expandByScalar(capsuleInfo.radius);\r\n\r\n const bvh = this.collider?.geometry as any;\r\n bvh?.boundsTree?.shapecast({\r\n // 检测包围盒碰撞\r\n intersectsBounds: (box: THREE.Box3) => box.intersectsBox(this.tempBox),\r\n // 检测三角形碰撞\r\n intersectsTriangle: (tri: any) => {\r\n const triPoint = this.tempVector;\r\n const capsulePoint = this.tempVector2;\r\n const distance = tri.closestPointToSegment(\r\n this.tempSegment,\r\n triPoint,\r\n capsulePoint\r\n );\r\n // 距离小于人物半径,发生碰撞\r\n if (distance < capsuleInfo.radius) {\r\n const depth = capsuleInfo.radius - distance;\r\n const direction = capsulePoint.sub(triPoint).normalize();\r\n this.tempSegment.start.addScaledVector(direction, depth);\r\n this.tempSegment.end.addScaledVector(direction, depth);\r\n }\r\n },\r\n });\r\n\r\n // 设置玩家位置\r\n const newPosition = this.tempVector\r\n .copy(this.tempSegment.start)\r\n .applyMatrix4(this.collider!.matrixWorld);\r\n const deltaVector = this.tempVector2.subVectors(\r\n newPosition,\r\n this.player.position\r\n );\r\n // 应用位移\r\n const offset = Math.max(0, deltaVector.length() - 1e-5);\r\n deltaVector.normalize().multiplyScalar(offset);\r\n this.player.position.add(deltaVector);\r\n\r\n // 第三人称-朝向\r\n if (!this.isFirstPerson && this.moveDir.lengthSq() > 0 && !this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position.clone().add(this.moveDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 飞行\r\n if (this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position\r\n .clone()\r\n .add(this.fwdPressed ? this.moveDir : this.camDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 第三人称-相机跟随\r\n if (!this.isFirstPerson) {\r\n const lookTarget = this.player.position.clone();\r\n lookTarget.y += 30 * this.playerModel.scale;\r\n this.camera.position.sub(this.controls.target); // 减去控制器向量\r\n this.controls.target.copy(lookTarget); // 设置控制器目标\r\n this.camera.position.add(lookTarget); // 设置相机位置\r\n this.controls.update(); // 更新控制器\r\n\r\n // 当视线被遮挡时判断\r\n this._personToCam.subVectors(this.camera.position, this.player.position); // 计算从player指向camera的向量(camera - player)\r\n const origin = this.player.position\r\n .clone()\r\n .add(new THREE.Vector3(0, 0, 0)); // 射线起点\r\n const direction = this._personToCam.clone().normalize(); // 方向\r\n const desiredDist = this._personToCam.length(); // 与期望距离\r\n this._raycasterPersonToCam.set(origin, direction);\r\n this._raycasterPersonToCam.far = desiredDist;\r\n\r\n // 做相交检测\r\n const intersects = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 相机拉近\r\n const hit = intersects[0]; // 找到第一个命中\r\n const safeDist = Math.max(\r\n hit.distance - this._camEpsilon,\r\n this._minCamDistance\r\n ); // 计算安全距离(hit.distance是从origin到碰撞点的距离)\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist)); // 目标相机位置 = origin + direction * safeDist\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp); // 平滑移动相机到targetCamPos\r\n } else {\r\n // 相机恢复\r\n // const dis = this.player.position.distanceTo(this.camera.position); // 计算当前人物到相机距离\r\n this._raycasterPersonToCam.far = this._maxCamDistance;\r\n // 检查预设相机位置是否有遮挡\r\n const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n // 恢复相机\r\n let safeDist = this._maxCamDistance;\r\n if (intersectsMaxDis.length) {\r\n const hitMax = intersectsMaxDis[0]; // 找到第一个命中\r\n safeDist = hitMax.distance - this._camEpsilon;\r\n }\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist));\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp);\r\n }\r\n }\r\n\r\n // 掉出场景重置\r\n if (this.player.position.y < this.boundingBoxMinY - 1) {\r\n // 检测当前位置与碰撞体是否相交\r\n this._originTmp.set(\r\n this.player.position.x,\r\n 10000,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 出现碰撞 说明玩家为bug意外掉落\r\n console.log(\"玩家为bug意外掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n intersects[0].point.y + 5,\r\n this.player.position.z\r\n )\r\n );\r\n } else {\r\n // 无碰撞 正常掉落\r\n console.log(\"玩家正常掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n this.player.position.y + 15,\r\n this.player.position.z\r\n )\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 重置 / 销毁\r\n reset(position?: THREE.Vector3) {\r\n if (!this.player) return;\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.copy(position ? position : this.initPos);\r\n }\r\n\r\n // 销毁\r\n destroy() {\r\n this.offAllEvent();\r\n if (this.player) {\r\n this.player.remove(this.camera);\r\n this.scene.remove(this.player);\r\n }\r\n (this.player as any) = null;\r\n if (this.person) {\r\n this.scene.remove(this.person);\r\n this.person = null;\r\n }\r\n\r\n this.resetControls();\r\n\r\n // 清理 BVH 可视化\r\n if (this.visualizer) {\r\n this.scene.remove(this.visualizer);\r\n this.visualizer = null;\r\n }\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n\r\n controllerInstance = null;\r\n }\r\n\r\n // 事件绑定\r\n onAllEvent() {\r\n this.isupdate = true;\r\n document.body.requestPointerLock();\r\n window.addEventListener(\"keydown\", this._boundOnKeydown);\r\n window.addEventListener(\"keyup\", this._boundOnKeyup);\r\n window.addEventListener(\"mousemove\", this._mouseMove);\r\n window.addEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 事件解绑\r\n offAllEvent() {\r\n this.isupdate = false;\r\n document.exitPointerLock();\r\n window.removeEventListener(\"keydown\", this._boundOnKeydown);\r\n window.removeEventListener(\"keyup\", this._boundOnKeyup);\r\n window.removeEventListener(\"mousemove\", this._mouseMove);\r\n window.removeEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 键盘按下事件\r\n private _boundOnKeydown = async (e: KeyboardEvent) => {\r\n if (\r\n e.ctrlKey &&\r\n (e.code === \"KeyW\" ||\r\n e.code === \"KeyA\" ||\r\n e.code === \"KeyS\" ||\r\n e.code === \"KeyD\")\r\n ) {\r\n e.preventDefault();\r\n }\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = true;\r\n if (!this.playerIsOnGround || this.isFlying) return;\r\n this.playPersonAnimationByName(\"jumping\");\r\n this.playerVelocity.y = this.jumpHeight;\r\n this.playerIsOnGround = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = true;\r\n break;\r\n case \"KeyV\":\r\n this.changeView();\r\n break;\r\n case \"KeyF\":\r\n this.isFlying = !this.isFlying;\r\n this.setAnimationByPressed();\r\n break;\r\n }\r\n };\r\n\r\n // 键盘抬起事件\r\n private _boundOnKeyup = (e: KeyboardEvent) => {\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = false;\r\n break;\r\n }\r\n };\r\n\r\n // 根据按键设置人物动画\r\n setAnimationByPressed = () => {\r\n this._maxCamDistance = this.orginMaxCamDistance;\r\n if (this.isFlying) {\r\n if (!this.fwdPressed) {\r\n this.playPersonAnimationByName(\"flyidle\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"flying\");\r\n // this._maxCamDistance = 700 * this.playerModel.scale;\r\n this._maxCamDistance = this.orginMaxCamDistance * 2;\r\n return;\r\n }\r\n\r\n if (this.playerIsOnGround) {\r\n if (\r\n !this.fwdPressed &&\r\n !this.bkdPressed &&\r\n !this.lftPressed &&\r\n !this.rgtPressed\r\n ) {\r\n this.playPersonAnimationByName(\"idle\");\r\n return;\r\n }\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第三人称下动画统一使用 前进 动画\r\n if (\r\n !this.isFirstPerson &&\r\n (this.lftPressed || this.rgtPressed || this.bkdPressed)\r\n ) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第一人称下根据方向播放不同动画\r\n if (this.lftPressed) {\r\n this.playPersonAnimationByName(\"left_walking\");\r\n return;\r\n }\r\n if (this.rgtPressed) {\r\n this.playPersonAnimationByName(\"right_walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n } else {\r\n this.playPersonAnimationByName(\"jumping\");\r\n }\r\n };\r\n\r\n // 鼠标移动事件\r\n private _mouseMove = (e: MouseEvent) => {\r\n // 记录状态\r\n if (document.pointerLockElement !== document.body) return;\r\n if (this.isFirstPerson) {\r\n const yaw = -e.movementX * 0.0001 * this.mouseSensity;\r\n const pitch = -e.movementY * 0.0001 * this.mouseSensity;\r\n this.player.rotateY(yaw);\r\n this.camera.rotation.x = THREE.MathUtils.clamp(\r\n this.camera.rotation.x + pitch,\r\n -1.3,\r\n 1.4\r\n );\r\n } else {\r\n const sensitivity = 0.0001 * this.mouseSensity;\r\n const deltaX = -e.movementX * sensitivity;\r\n const deltaY = -e.movementY * sensitivity;\r\n // 获取目标点\r\n const target = this.player.position.clone();\r\n // 计算相机到目标的距离\r\n const distance = this.camera.position.distanceTo(target);\r\n // 计算当前角度\r\n const currentPosition = this.camera.position.clone().sub(target);\r\n let theta = Math.atan2(currentPosition.x, currentPosition.z);\r\n let phi = Math.acos(currentPosition.y / distance);\r\n // 应用旋转\r\n theta += deltaX;\r\n phi += deltaY;\r\n // 限制phi角度\r\n phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));\r\n // 计算新的相机位置\r\n const newX = distance * Math.sin(phi) * Math.sin(theta);\r\n const newY = distance * Math.cos(phi);\r\n const newZ = distance * Math.sin(phi) * Math.cos(theta);\r\n\r\n this.camera.position.set(\r\n target.x + newX,\r\n target.y + newY,\r\n target.z + newZ\r\n );\r\n this.camera.lookAt(target);\r\n }\r\n };\r\n\r\n private _mouseClick = (e: MouseEvent) => {\r\n if (document.pointerLockElement !== document.body)\r\n document.body.requestPointerLock();\r\n };\r\n\r\n // 更新模型动画\r\n private updateMixers(delta: number) {\r\n if (this.personMixer) this.personMixer.update(delta);\r\n }\r\n\r\n // BVH构建\r\n async createBVH(meshUrl: string = \"\"): Promise<void> {\r\n await this.initLoader(); // 初始化加载器\r\n\r\n const ensureAttributesMinimal = (\r\n geom: THREE.BufferGeometry\r\n ): THREE.BufferGeometry | null => {\r\n if (!geom.attributes.position) {\r\n // console.warn(\"跳过无 position 的几何体\", geom);\r\n return null;\r\n }\r\n if (!geom.attributes.normal) geom.computeVertexNormals();\r\n if (!geom.attributes.uv) {\r\n const count = geom.attributes.position.count;\r\n const dummyUV = new Float32Array(count * 2);\r\n geom.setAttribute(\"uv\", new THREE.BufferAttribute(dummyUV, 2));\r\n }\r\n return geom;\r\n };\r\n\r\n const collected: THREE.BufferGeometry[] = [];\r\n if (meshUrl == \"\") {\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n this.scene.traverse((c) => {\r\n const mesh = c as THREE.Mesh;\r\n if (mesh?.isMesh && mesh.geometry && c.name !== \"capsule\") {\r\n try {\r\n let geom = (mesh.geometry as THREE.BufferGeometry).clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n } catch (e) {\r\n console.warn(\"处理网格时出错:\", mesh, e);\r\n }\r\n }\r\n });\r\n\r\n if (!collected.length) {\r\n return;\r\n }\r\n\r\n // 统一属性集合\r\n type AttrMeta = { itemSize: number; arrayCtor: any; examples: number };\r\n const attrMap = new Map<string, AttrMeta>();\r\n const attrConflict = new Set<string>();\r\n\r\n for (const g of collected) {\r\n for (const name of Object.keys(g.attributes)) {\r\n const attr = g.attributes[name] as THREE.BufferAttribute;\r\n const ctor = (attr.array as any).constructor;\r\n const itemSize = attr.itemSize;\r\n if (!attrMap.has(name)) {\r\n attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });\r\n } else {\r\n const m = attrMap.get(name)!;\r\n if (m.itemSize !== itemSize || m.arrayCtor !== ctor)\r\n attrConflict.add(name);\r\n else m.examples++;\r\n }\r\n }\r\n }\r\n\r\n if (attrConflict.size) {\r\n for (const g of collected) {\r\n for (const name of Array.from(attrConflict)) {\r\n if (g.attributes[name]) g.deleteAttribute(name);\r\n }\r\n }\r\n for (const name of attrConflict) attrMap.delete(name);\r\n }\r\n\r\n const attrNames = Array.from(attrMap.keys());\r\n for (const g of collected) {\r\n const count = g.attributes.position.count;\r\n for (const name of attrNames) {\r\n if (!g.attributes[name]) {\r\n const meta = attrMap.get(name)!;\r\n const len = count * meta.itemSize;\r\n const array = new meta.arrayCtor(len);\r\n g.setAttribute(\r\n name,\r\n new THREE.BufferAttribute(array, meta.itemSize)\r\n );\r\n }\r\n }\r\n }\r\n } else {\r\n const gltf: GLTF = await this.loader.loadAsync(meshUrl, (xhr) => {});\r\n const mesh = gltf.scene.children[0] as THREE.Mesh;\r\n mesh.name = \"BVH加载模型\";\r\n\r\n // 推入几何体\r\n let geom = mesh.geometry.clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n }\r\n\r\n // 合并几何体\r\n const merged = BufferGeometryUtils.mergeGeometries(collected, false);\r\n if (!merged) {\r\n console.error(\"合并几何失败\");\r\n return;\r\n }\r\n\r\n // 构建bvh\r\n (merged as any).boundsTree = new MeshBVH(merged);\r\n this.collider = new THREE.Mesh(\r\n merged,\r\n new THREE.MeshBasicMaterial({\r\n opacity: 0.5,\r\n transparent: true,\r\n wireframe: true,\r\n })\r\n );\r\n\r\n if (this.displayCollider) this.scene.add(this.collider);\r\n if (this.displayVisualizer) {\r\n if (this.visualizer) this.scene.remove(this.visualizer);\r\n this.visualizer = new MeshBVHHelper(this.collider, this.visualizeDepth);\r\n this.scene.add(this.visualizer);\r\n }\r\n this.boundingBoxMinY = (this.collider as any).geometry.boundingBox.min.y;\r\n console.log(\"bvh加载模型成功\", this.collider);\r\n }\r\n}\r\n\r\n// 导出API\r\nexport function playerController() {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n const c = controllerInstance;\r\n return {\r\n init: (\r\n opts: {\r\n scene: THREE.Scene;\r\n camera: THREE.PerspectiveCamera;\r\n controls: OrbitControls;\r\n playerModel: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n initPos?: THREE.Vector3;\r\n mouseSensity?: number;\r\n minCamDistance?: number;\r\n maxCamDistance?: number;\r\n },\r\n callback?: () => void\r\n ) => c.init(opts, callback),\r\n changeView: () => c.changeView(),\r\n createBVH: (url: string = \"\") => c.createBVH(url),\r\n createPlayer: () => c.createPlayer(),\r\n reset: (pos?: THREE.Vector3) => c.reset(pos),\r\n update: (dt?: number) => c.update(dt),\r\n destroy: () => c.destroy(),\r\n displayCollider: c.displayCollider,\r\n displayPlayer: c.displayPlayer,\r\n displayVisualizer: c.displayVisualizer,\r\n };\r\n}\r\n\r\n// 打开所有事件\r\nexport function onAllEvent(): void {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n controllerInstance.onAllEvent();\r\n}\r\n\r\n// 关闭所有事件\r\nexport function offAllEvent(): void {\r\n if (!controllerInstance) return;\r\n controllerInstance.offAllEvent();\r\n}\r\n"],"mappings":"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","names":["angle","intersects"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/playerController.ts"],"sourcesContent":["export * from \"./playerController\";\r\n","import * as THREE from \"three\";\r\nimport { acceleratedRaycast, MeshBVH, MeshBVHHelper } from \"three-mesh-bvh\";\r\nimport type { GLTF } from \"three/examples/jsm/Addons.js\";\r\nimport { OrbitControls } from \"three/examples/jsm/controls/OrbitControls.js\";\r\nimport { RoundedBoxGeometry } from \"three/examples/jsm/geometries/RoundedBoxGeometry.js\";\r\nimport { DRACOLoader } from \"three/examples/jsm/loaders/DRACOLoader.js\";\r\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\r\nimport * as BufferGeometryUtils from \"three/examples/jsm/utils/BufferGeometryUtils.js\";\r\n\r\nTHREE.Mesh.prototype.raycast = acceleratedRaycast;\r\n\r\nlet controllerInstance: PlayerController | null = null;\r\nconst clock = new THREE.Clock();\r\n\r\ntype PlayerControllerOptions = {\r\n scene: THREE.Scene;\r\n camera: THREE.PerspectiveCamera;\r\n controls: OrbitControls;\r\n playerModel: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n initPos?: THREE.Vector3;\r\n mouseSensity?: number;\r\n minCamDistance?: number;\r\n maxCamDistance?: number;\r\n colliderMeshUrl?: string;\r\n};\r\n\r\nclass PlayerController {\r\n loader: GLTFLoader = new GLTFLoader();\r\n // 基本配置与参数\r\n scene!: THREE.Scene;\r\n camera!: THREE.PerspectiveCamera;\r\n controls!: OrbitControls;\r\n initPos!: THREE.Vector3;\r\n playerModel!: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n visualizeDepth!: number;\r\n gravity!: number;\r\n jumpHeight!: number;\r\n playerSpeed!: number;\r\n mouseSensity!: number;\r\n originPlayerSpeed!: number;\r\n colliderMeshUrl!: string;\r\n\r\n playerRadius: number = 45;\r\n playerHeight: number = 180;\r\n isFirstPerson: boolean = false;\r\n boundingBoxMinY: number = 0;\r\n // 测试参数\r\n displayPlayer: boolean = false;\r\n displayCollider: boolean = false;\r\n displayVisualizer: boolean = false;\r\n\r\n // 场景对象\r\n collider: THREE.Mesh | null = null;\r\n visualizer: MeshBVHHelper | null = null;\r\n player!: THREE.Mesh & { capsuleInfo?: any };\r\n person: THREE.Object3D | null = null;\r\n\r\n // 状态开关\r\n playerIsOnGround: boolean = false;\r\n isupdate: boolean = true;\r\n isFlying: boolean = false;\r\n\r\n // 输入状态\r\n fwdPressed: boolean = false;\r\n bkdPressed: boolean = false;\r\n lftPressed: boolean = false;\r\n rgtPressed: boolean = false;\r\n spacePressed: boolean = false;\r\n ctPressed: boolean = false;\r\n shiftPressed: boolean = false;\r\n\r\n // 第三人称\r\n _camCollisionLerp: number = 0.18; // 平滑系数\r\n _camEpsilon: number = 0.35; // 摄像机与障碍物之间的安全距离(米)\r\n _minCamDistance: number = 1.0; // 摄像机最小距离\r\n _maxCamDistance: number = 4.4; // 摄像机最大距离\r\n orginMaxCamDistance: number = 4.4;\r\n\r\n // 物理/运动\r\n playerVelocity = new THREE.Vector3(); // 玩家速度向量\r\n readonly upVector = new THREE.Vector3(0, 1, 0);\r\n\r\n // 临时复用向量/矩阵\r\n readonly tempVector = new THREE.Vector3();\r\n readonly tempVector2 = new THREE.Vector3();\r\n readonly tempBox = new THREE.Box3();\r\n readonly tempMat = new THREE.Matrix4();\r\n readonly tempSegment = new THREE.Line3();\r\n\r\n // 动画相关\r\n personMixer?: THREE.AnimationMixer;\r\n personActions?: Map<string, THREE.AnimationAction>;\r\n idleAction!: THREE.AnimationAction;\r\n walkAction!: THREE.AnimationAction;\r\n leftWalkAction!: THREE.AnimationAction;\r\n rightWalkAction!: THREE.AnimationAction;\r\n backwardAction!: THREE.AnimationAction;\r\n jumpAction!: THREE.AnimationAction;\r\n runAction!: THREE.AnimationAction;\r\n flyidleAction!: THREE.AnimationAction;\r\n flyAction!: THREE.AnimationAction;\r\n controlDroneAction!: THREE.AnimationAction;\r\n actionState!: THREE.AnimationAction;\r\n\r\n // 复用向量:用于相机朝向 / 移动\r\n readonly camDir = new THREE.Vector3();\r\n readonly moveDir = new THREE.Vector3();\r\n readonly targetQuat = new THREE.Quaternion();\r\n readonly targetMat = new THREE.Matrix4();\r\n readonly rotationSpeed = 10;\r\n readonly DIR_FWD = new THREE.Vector3(0, 0, -1);\r\n readonly DIR_BKD = new THREE.Vector3(0, 0, 1);\r\n readonly DIR_LFT = new THREE.Vector3(-1, 0, 0);\r\n readonly DIR_RGT = new THREE.Vector3(1, 0, 0);\r\n readonly DIR_UP = new THREE.Vector3(0, 1, 0);\r\n\r\n readonly _personToCam = new THREE.Vector3();\r\n\r\n readonly _originTmp = new THREE.Vector3();\r\n readonly _raycaster = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -1, 0)\r\n );\r\n readonly _raycasterPersonToCam = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3()\r\n );\r\n\r\n // 射线检测时只返回第一个碰撞\r\n constructor() {\r\n (this._raycaster as any).firstHitOnly = true;\r\n (this._raycasterPersonToCam as any).firstHitOnly = true;\r\n }\r\n\r\n // 初始化\r\n async init(opts: PlayerControllerOptions, callback?: () => void) {\r\n this.scene = opts.scene;\r\n this.camera = opts.camera;\r\n this.camera.rotation.order = \"YXZ\";\r\n this.controls = opts.controls;\r\n this.playerModel = opts.playerModel;\r\n this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);\r\n this.mouseSensity = opts.mouseSensity ? opts.mouseSensity : 5;\r\n\r\n const s = this.playerModel.scale;\r\n this.visualizeDepth = 0 * s;\r\n this.gravity = opts.playerModel.gravity\r\n ? opts.playerModel.gravity * s\r\n : -2400 * s;\r\n this.jumpHeight = opts.playerModel.jumpHeight\r\n ? opts.playerModel.jumpHeight * s\r\n : 800 * s;\r\n this.originPlayerSpeed = opts.playerModel.speed\r\n ? opts.playerModel.speed * s\r\n : 400 * s;\r\n this.playerSpeed = this.originPlayerSpeed;\r\n\r\n this._camCollisionLerp = 0.18;\r\n this._camEpsilon = 35 * s;\r\n this._minCamDistance = opts.minCamDistance\r\n ? opts.minCamDistance * s\r\n : 100 * s;\r\n this._maxCamDistance = opts.maxCamDistance\r\n ? opts.maxCamDistance * s\r\n : 440 * s;\r\n this.orginMaxCamDistance = this._maxCamDistance;\r\n\r\n // 创建bvh\r\n await this.createBVH(opts.colliderMeshUrl);\r\n\r\n // 创建玩家\r\n this.createPlayer();\r\n\r\n // 加载玩家模型\r\n await this.loadPersonGLB();\r\n\r\n // 等待资源加载完毕再设置摄像机\r\n if (this.isFirstPerson && this.player) {\r\n this.player.add(this.camera);\r\n }\r\n this.onAllEvent(); // 绑定事件\r\n this.setCameraPos();\r\n this.setControls();\r\n if (callback) callback();\r\n }\r\n\r\n // 第一/三视角切换\r\n changeView() {\r\n this.isFirstPerson = !this.isFirstPerson;\r\n if (this.isFirstPerson) {\r\n this.player.attach(this.camera);\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n this.camera.rotation.set(0, Math.PI, 0);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n } else {\r\n this.scene.attach(this.camera);\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n this.controls.target.copy(worldPos);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n }\r\n }\r\n\r\n // 摄像机/控制器设置\r\n setCameraPos() {\r\n if (this.isFirstPerson) {\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n } else {\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n }\r\n this.camera.updateProjectionMatrix();\r\n }\r\n\r\n // 设置控制器\r\n setControls() {\r\n this.controls.enabled = false;\r\n this.controls.maxPolarAngle = Math.PI * (300 / 360);\r\n }\r\n\r\n // 重置控制器\r\n resetControls() {\r\n if (!this.controls) return;\r\n this.controls.enabled = true;\r\n this.controls.enablePan = true;\r\n this.controls.maxPolarAngle = Math.PI / 2;\r\n this.controls.rotateSpeed = 1;\r\n this.controls.enableZoom = true;\r\n this.controls.mouseButtons = {\r\n LEFT: 0,\r\n MIDDLE: 1,\r\n RIGHT: 2,\r\n };\r\n }\r\n\r\n // 初始化加载器\r\n async initLoader() {\r\n const dracoLoader = new DRACOLoader();\r\n dracoLoader.setDecoderPath(\r\n \"https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/\"\r\n );\r\n dracoLoader.setDecoderConfig({ type: \"js\" });\r\n this.loader.setDRACOLoader(dracoLoader);\r\n }\r\n\r\n // 人物与动画加载\r\n async loadPersonGLB() {\r\n try {\r\n const gltf: GLTF = await this.loader.loadAsync(this.playerModel.url);\r\n this.person = gltf.scene;\r\n const sc = this.playerModel.scale;\r\n const h = this.playerHeight * sc;\r\n this.person.scale.set(sc, sc, sc);\r\n this.person.position.set(0, -h * 0.75, 0);\r\n this.person.traverse((child: any) => {\r\n if (child.isMesh) {\r\n child.castShadow = true;\r\n\r\n const mat = child.material;\r\n if (!mat) return;\r\n\r\n // 处理多材质的情况\r\n const mats = Array.isArray(mat) ? mat : [mat];\r\n\r\n mats.forEach((m) => {\r\n // console.log(\"mat\", m);\r\n // m.envMap = this.scene.background;\r\n // m.envMapIntensity = 1.0;\r\n });\r\n }\r\n });\r\n this.player.add(this.person);\r\n this.reset();\r\n\r\n // 创建人物 mixer 与 actions\r\n this.personMixer = new THREE.AnimationMixer(this.person);\r\n const animations = gltf.animations ?? [];\r\n this.personActions = new Map<string, THREE.AnimationAction>();\r\n // 取出动作并注册到 map\r\n const findClip = (name: string) =>\r\n animations.find((a: any) => a.name === name);\r\n const regs: [string, string][] = [\r\n [this.playerModel.idleAnim, \"idle\"],\r\n [this.playerModel.walkAnim, \"walking\"],\r\n [\r\n this.playerModel.leftWalkAnim || this.playerModel.walkAnim,\r\n \"left_walking\",\r\n ],\r\n [\r\n this.playerModel.rightWalkAnim || this.playerModel.walkAnim,\r\n \"right_walking\",\r\n ],\r\n [\r\n this.playerModel.backwardAnim || this.playerModel.walkAnim,\r\n \"walking_backward\",\r\n ],\r\n [this.playerModel.jumpAnim, \"jumping\"],\r\n [this.playerModel.runAnim, \"running\"],\r\n [this.playerModel.flyIdleAnim || this.playerModel.idleAnim, \"flyidle\"],\r\n [this.playerModel.flyAnim || this.playerModel.idleAnim, \"flying\"],\r\n ];\r\n\r\n // 注册动作并设置循环模式\r\n for (const [key, clipName] of regs) {\r\n const clip = findClip(key);\r\n if (!clip) continue;\r\n const action = this.personMixer.clipAction(clip);\r\n\r\n if (clipName === \"jumping\") {\r\n action.setLoop(THREE.LoopOnce, 1); // 播放一次\r\n action.clampWhenFinished = true;\r\n action.setEffectiveTimeScale(1.2); // 播放速度\r\n } else {\r\n action.setLoop(THREE.LoopRepeat, Infinity); // 循环播放\r\n action.clampWhenFinished = false;\r\n action.setEffectiveTimeScale(1);\r\n }\r\n\r\n action.enabled = true; // 激活\r\n action.setEffectiveWeight(0); // 初始权重为0\r\n this.personActions.set(clipName, action);\r\n }\r\n\r\n // 把actions激活\r\n this.idleAction = this.personActions.get(\"idle\")!;\r\n this.walkAction = this.personActions.get(\"walking\")!;\r\n this.leftWalkAction = this.personActions.get(\"left_walking\")!;\r\n this.rightWalkAction = this.personActions.get(\"right_walking\")!;\r\n this.backwardAction = this.personActions.get(\"walking_backward\")!;\r\n this.jumpAction = this.personActions.get(\"jumping\")!;\r\n this.runAction = this.personActions.get(\"running\")!;\r\n this.flyidleAction = this.personActions.get(\"flyidle\")!;\r\n this.flyAction = this.personActions.get(\"flying\")!;\r\n\r\n // 激活空闲动作\r\n this.idleAction.setEffectiveWeight(1);\r\n this.idleAction.play();\r\n this.actionState = this.idleAction;\r\n\r\n this.personMixer.addEventListener(\"finished\", (ev: any) => {\r\n const finishedAction: THREE.AnimationAction = ev.action;\r\n\r\n if (finishedAction === this.jumpAction) {\r\n // jump 播放结束后的逻辑\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) this.playPersonAnimationByName(\"running\");\r\n else this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n if (this.rgtPressed || this.lftPressed) {\r\n this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"idle\");\r\n }\r\n });\r\n } catch (error) {}\r\n }\r\n\r\n // 平滑切换人物动画\r\n playPersonAnimationByName(name: string, fade = 0.18) {\r\n // console.log(\"播放动画\", name);\r\n if (!this.personActions) return;\r\n if (this.ctPressed) return;\r\n\r\n const next = this.personActions.get(name);\r\n if (!next) return;\r\n\r\n // 如果是同一个action,直接返回\r\n if (this.actionState === next) return;\r\n\r\n const prev = this.actionState;\r\n\r\n // 对于一次性动作先reset()\r\n next.reset();\r\n next.setEffectiveWeight(1);\r\n next.play();\r\n\r\n if (prev && prev !== next) {\r\n // 让 prev 淡出,next 淡入\r\n prev.fadeOut(fade);\r\n next.fadeIn(fade);\r\n } else {\r\n // 时直接淡入\r\n next.fadeIn(fade);\r\n }\r\n\r\n this.actionState = next;\r\n }\r\n\r\n // 创建玩家胶囊体\r\n createPlayer() {\r\n const material = new THREE.MeshStandardMaterial({\r\n color: new THREE.Color(1, 0, 0),\r\n shadowSide: THREE.DoubleSide,\r\n depthTest: false,\r\n });\r\n material.transparent = true;\r\n material.opacity = this.displayPlayer ? 0.5 : 0;\r\n material.wireframe = true;\r\n material.depthWrite = false;\r\n\r\n const r = this.playerRadius * this.playerModel.scale;\r\n const h = this.playerHeight * this.playerModel.scale;\r\n this.player = new THREE.Mesh(\r\n new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75),\r\n material\r\n ) as typeof this.player;\r\n\r\n this.player.geometry.translate(0, -h * 0.25, 0);\r\n this.player.capsuleInfo = {\r\n radius: r,\r\n segment: new THREE.Line3(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -h * 0.5, 0)\r\n ),\r\n };\r\n\r\n this.player.name = \"capsule\";\r\n this.scene.add(this.player);\r\n this.reset();\r\n }\r\n\r\n // 获取法线与Y轴的夹角\r\n getAngleWithYAxis(normal: { x: number; y: number; z: number }): number {\r\n // Y轴正方向向量\r\n const yAxis = { x: 0, y: 1, z: 0 };\r\n\r\n // 向量点积\r\n const dotProduct =\r\n normal.x * yAxis.x + normal.y * yAxis.y + normal.z * yAxis.z;\r\n\r\n // 向量模长\r\n const normalMagnitude = Math.sqrt(\r\n normal.x * normal.x + normal.y * normal.y + normal.z * normal.z\r\n );\r\n const yAxisMagnitude = 1; // Y轴单位向量长度为1\r\n\r\n // 计算夹角余弦值\r\n const cosTheta = dotProduct / (normalMagnitude * yAxisMagnitude);\r\n\r\n // 返回夹角(弧度)\r\n return Math.acos(cosTheta);\r\n }\r\n\r\n // 每帧更新\r\n async update(delta: number = clock.getDelta()) {\r\n if (!this.isupdate || !this.player || !this.collider) return;\r\n delta = Math.min(delta, 1 / 30);\r\n\r\n if (!this.isFlying)\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.updateMixers(delta);\r\n this.camera.getWorldDirection(this.camDir);\r\n let angle = Math.atan2(this.camDir.z, this.camDir.x) + Math.PI / 2;\r\n angle = 2 * Math.PI - angle;\r\n\r\n this.moveDir.set(0, 0, 0);\r\n if (this.fwdPressed) this.moveDir.add(this.DIR_FWD);\r\n if (this.bkdPressed) this.moveDir.add(this.DIR_BKD);\r\n if (this.lftPressed) this.moveDir.add(this.DIR_LFT);\r\n if (this.rgtPressed) this.moveDir.add(this.DIR_RGT);\r\n if (this.isFlying) {\r\n if (this.fwdPressed) {\r\n this.moveDir.y = this.camDir.y;\r\n } else {\r\n this.moveDir.y = 0;\r\n }\r\n if (this.spacePressed) {\r\n this.moveDir.add(this.DIR_UP);\r\n }\r\n }\r\n // 设置速度\r\n if (this.isFlying && this.fwdPressed) {\r\n this.playerSpeed = this.shiftPressed\r\n ? this.originPlayerSpeed * 12\r\n : this.originPlayerSpeed * 7;\r\n } else {\r\n this.playerSpeed = this.shiftPressed\r\n ? this.originPlayerSpeed * 2\r\n : this.originPlayerSpeed;\r\n }\r\n this.moveDir.normalize().applyAxisAngle(this.upVector, angle);\r\n this.player.position.addScaledVector(\r\n this.moveDir,\r\n this.playerSpeed * delta\r\n );\r\n\r\n // 向下射线检测地面高度\r\n let playerDistanceFromGround = Infinity;\r\n this._originTmp.set(\r\n this.player.position.x,\r\n this.player.position.y,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n playerDistanceFromGround = this.player.position.y - intersects[0].point.y;\r\n const normal = intersects[0].normal as THREE.Vector3;\r\n const angle = (this.getAngleWithYAxis(normal) * 180) / Math.PI;\r\n const maxH = this.playerHeight * this.playerModel.scale * 0.9; // 坡度高度阈值\r\n const h = this.playerHeight * this.playerModel.scale * 0.75; // 正常高度\r\n const minH = this.playerHeight * this.playerModel.scale * 0.7; // 最小高度\r\n\r\n if (this.isFlying) {\r\n } else {\r\n if (playerDistanceFromGround > maxH) {\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = false;\r\n } else if (\r\n playerDistanceFromGround > h &&\r\n playerDistanceFromGround < maxH\r\n ) {\r\n if (angle >= 0 && angle < 5) {\r\n // 平地\r\n // this.player.position.y = intersects[0].point.y + h;\r\n // this.playerVelocity.set(0, 0, 0);\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = true;\r\n } else {\r\n // 坡地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n }\r\n } else if (\r\n playerDistanceFromGround > minH &&\r\n playerDistanceFromGround < h\r\n ) {\r\n // 误差范围内 在平地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n } else if (playerDistanceFromGround < minH) {\r\n // 强行拉回\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.set(\r\n this.player.position.x,\r\n intersects[0].point.y + h,\r\n this.player.position.z\r\n );\r\n this.playerIsOnGround = true;\r\n }\r\n }\r\n }\r\n\r\n // 更新玩家矩阵\r\n this.player.updateMatrixWorld();\r\n // 碰撞检测\r\n const capsuleInfo = this.player.capsuleInfo;\r\n this.tempBox.makeEmpty();\r\n this.tempMat.copy(this.collider!.matrixWorld).invert();\r\n this.tempSegment.copy(capsuleInfo.segment);\r\n this.tempSegment.start\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n this.tempSegment.end\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n\r\n this.tempBox.expandByPoint(this.tempSegment.start);\r\n this.tempBox.expandByPoint(this.tempSegment.end);\r\n this.tempBox.expandByScalar(capsuleInfo.radius);\r\n\r\n const bvh = this.collider?.geometry as any;\r\n bvh?.boundsTree?.shapecast({\r\n // 检测包围盒碰撞\r\n intersectsBounds: (box: THREE.Box3) => box.intersectsBox(this.tempBox),\r\n // 检测三角形碰撞\r\n intersectsTriangle: (tri: any) => {\r\n const triPoint = this.tempVector;\r\n const capsulePoint = this.tempVector2;\r\n const distance = tri.closestPointToSegment(\r\n this.tempSegment,\r\n triPoint,\r\n capsulePoint\r\n );\r\n // 距离小于人物半径,发生碰撞\r\n if (distance < capsuleInfo.radius) {\r\n const depth = capsuleInfo.radius - distance;\r\n const direction = capsulePoint.sub(triPoint).normalize();\r\n this.tempSegment.start.addScaledVector(direction, depth);\r\n this.tempSegment.end.addScaledVector(direction, depth);\r\n }\r\n },\r\n });\r\n\r\n // 设置玩家位置\r\n const newPosition = this.tempVector\r\n .copy(this.tempSegment.start)\r\n .applyMatrix4(this.collider!.matrixWorld);\r\n const deltaVector = this.tempVector2.subVectors(\r\n newPosition,\r\n this.player.position\r\n );\r\n // 应用位移\r\n const offset = Math.max(0, deltaVector.length() - 1e-5);\r\n deltaVector.normalize().multiplyScalar(offset);\r\n this.player.position.add(deltaVector);\r\n\r\n // 第三人称-朝向\r\n if (!this.isFirstPerson && this.moveDir.lengthSq() > 0 && !this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position.clone().add(this.moveDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 飞行\r\n if (this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position\r\n .clone()\r\n .add(this.fwdPressed ? this.moveDir : this.camDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 第三人称-相机跟随\r\n if (!this.isFirstPerson) {\r\n const lookTarget = this.player.position.clone();\r\n lookTarget.y += 30 * this.playerModel.scale;\r\n this.camera.position.sub(this.controls.target); // 减去控制器向量\r\n this.controls.target.copy(lookTarget); // 设置控制器目标\r\n this.camera.position.add(lookTarget); // 设置相机位置\r\n this.controls.update(); // 更新控制器\r\n\r\n // 当视线被遮挡时判断\r\n this._personToCam.subVectors(this.camera.position, this.player.position); // 计算从player指向camera的向量(camera - player)\r\n const origin = this.player.position\r\n .clone()\r\n .add(new THREE.Vector3(0, 0, 0)); // 射线起点\r\n const direction = this._personToCam.clone().normalize(); // 方向\r\n const desiredDist = this._personToCam.length(); // 与期望距离\r\n this._raycasterPersonToCam.set(origin, direction);\r\n this._raycasterPersonToCam.far = desiredDist;\r\n\r\n // 做相交检测\r\n const intersects = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 相机拉近\r\n const hit = intersects[0]; // 找到第一个命中\r\n const safeDist = Math.max(\r\n hit.distance - this._camEpsilon,\r\n this._minCamDistance\r\n ); // 计算安全距离(hit.distance是从origin到碰撞点的距离)\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist)); // 目标相机位置 = origin + direction * safeDist\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp); // 平滑移动相机到targetCamPos\r\n } else {\r\n // 相机恢复\r\n // const dis = this.player.position.distanceTo(this.camera.position); // 计算当前人物到相机距离\r\n this._raycasterPersonToCam.far = this._maxCamDistance;\r\n // 检查预设相机位置是否有遮挡\r\n const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n // 恢复相机\r\n let safeDist = this._maxCamDistance;\r\n if (intersectsMaxDis.length) {\r\n const hitMax = intersectsMaxDis[0]; // 找到第一个命中\r\n safeDist = hitMax.distance - this._camEpsilon;\r\n }\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist));\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp);\r\n }\r\n }\r\n\r\n // 掉出场景重置\r\n if (this.player.position.y < this.boundingBoxMinY - 1) {\r\n // 检测当前位置与碰撞体是否相交\r\n this._originTmp.set(\r\n this.player.position.x,\r\n 10000,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 出现碰撞 说明玩家为bug意外掉落\r\n console.log(\"玩家为bug意外掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n intersects[0].point.y + 5,\r\n this.player.position.z\r\n )\r\n );\r\n } else {\r\n // 无碰撞 正常掉落\r\n console.log(\"玩家正常掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n this.player.position.y + 15,\r\n this.player.position.z\r\n )\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 重置\r\n reset(position?: THREE.Vector3) {\r\n if (!this.player) return;\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.copy(position ? position : this.initPos);\r\n }\r\n\r\n // 销毁\r\n destroy() {\r\n this.offAllEvent();\r\n if (this.player) {\r\n this.player.remove(this.camera);\r\n this.scene.remove(this.player);\r\n }\r\n (this.player as any) = null;\r\n if (this.person) {\r\n this.scene.remove(this.person);\r\n this.person = null;\r\n }\r\n\r\n this.resetControls();\r\n\r\n // 清理 BVH 可视化\r\n if (this.visualizer) {\r\n this.scene.remove(this.visualizer);\r\n this.visualizer = null;\r\n }\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n\r\n controllerInstance = null;\r\n }\r\n\r\n // 事件绑定\r\n onAllEvent() {\r\n this.isupdate = true;\r\n document.body.requestPointerLock();\r\n window.addEventListener(\"keydown\", this._boundOnKeydown);\r\n window.addEventListener(\"keyup\", this._boundOnKeyup);\r\n window.addEventListener(\"mousemove\", this._mouseMove);\r\n window.addEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 事件解绑\r\n offAllEvent() {\r\n this.isupdate = false;\r\n document.exitPointerLock();\r\n window.removeEventListener(\"keydown\", this._boundOnKeydown);\r\n window.removeEventListener(\"keyup\", this._boundOnKeyup);\r\n window.removeEventListener(\"mousemove\", this._mouseMove);\r\n window.removeEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 键盘按下事件\r\n private _boundOnKeydown = async (e: KeyboardEvent) => {\r\n if (\r\n e.ctrlKey &&\r\n (e.code === \"KeyW\" ||\r\n e.code === \"KeyA\" ||\r\n e.code === \"KeyS\" ||\r\n e.code === \"KeyD\")\r\n ) {\r\n e.preventDefault();\r\n }\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = true;\r\n if (!this.playerIsOnGround || this.isFlying) return;\r\n this.playPersonAnimationByName(\"jumping\");\r\n this.playerVelocity.y = this.jumpHeight;\r\n this.playerIsOnGround = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = true;\r\n break;\r\n case \"KeyV\":\r\n this.changeView();\r\n break;\r\n case \"KeyF\":\r\n this.isFlying = !this.isFlying;\r\n this.setAnimationByPressed();\r\n break;\r\n }\r\n };\r\n\r\n // 键盘抬起事件\r\n private _boundOnKeyup = (e: KeyboardEvent) => {\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = false;\r\n break;\r\n }\r\n };\r\n\r\n // 根据按键设置人物动画\r\n setAnimationByPressed = () => {\r\n this._maxCamDistance = this.orginMaxCamDistance;\r\n if (this.isFlying) {\r\n if (!this.fwdPressed) {\r\n this.playPersonAnimationByName(\"flyidle\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"flying\");\r\n // this._maxCamDistance = 700 * this.playerModel.scale;\r\n this._maxCamDistance = this.orginMaxCamDistance * 2;\r\n return;\r\n }\r\n\r\n if (this.playerIsOnGround) {\r\n if (\r\n !this.fwdPressed &&\r\n !this.bkdPressed &&\r\n !this.lftPressed &&\r\n !this.rgtPressed\r\n ) {\r\n this.playPersonAnimationByName(\"idle\");\r\n return;\r\n }\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第三人称下动画统一使用 前进 动画\r\n if (\r\n !this.isFirstPerson &&\r\n (this.lftPressed || this.rgtPressed || this.bkdPressed)\r\n ) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第一人称下根据方向播放不同动画\r\n if (this.lftPressed) {\r\n this.playPersonAnimationByName(\"left_walking\");\r\n return;\r\n }\r\n if (this.rgtPressed) {\r\n this.playPersonAnimationByName(\"right_walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n } else {\r\n this.playPersonAnimationByName(\"jumping\");\r\n }\r\n };\r\n\r\n // 鼠标移动事件\r\n private _mouseMove = (e: MouseEvent) => {\r\n // 记录状态\r\n if (document.pointerLockElement !== document.body) return;\r\n if (this.isFirstPerson) {\r\n const yaw = -e.movementX * 0.0001 * this.mouseSensity;\r\n const pitch = -e.movementY * 0.0001 * this.mouseSensity;\r\n this.player.rotateY(yaw);\r\n this.camera.rotation.x = THREE.MathUtils.clamp(\r\n this.camera.rotation.x + pitch,\r\n -1.3,\r\n 1.4\r\n );\r\n } else {\r\n const sensitivity = 0.0001 * this.mouseSensity;\r\n const deltaX = -e.movementX * sensitivity;\r\n const deltaY = -e.movementY * sensitivity;\r\n // 获取目标点\r\n const target = this.player.position.clone();\r\n // 计算相机到目标的距离\r\n const distance = this.camera.position.distanceTo(target);\r\n // 计算当前角度\r\n const currentPosition = this.camera.position.clone().sub(target);\r\n let theta = Math.atan2(currentPosition.x, currentPosition.z);\r\n let phi = Math.acos(currentPosition.y / distance);\r\n // 应用旋转\r\n theta += deltaX;\r\n phi += deltaY;\r\n // 限制phi角度\r\n phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));\r\n // 计算新的相机位置\r\n const newX = distance * Math.sin(phi) * Math.sin(theta);\r\n const newY = distance * Math.cos(phi);\r\n const newZ = distance * Math.sin(phi) * Math.cos(theta);\r\n\r\n this.camera.position.set(\r\n target.x + newX,\r\n target.y + newY,\r\n target.z + newZ\r\n );\r\n this.camera.lookAt(target);\r\n }\r\n };\r\n\r\n private _mouseClick = (e: MouseEvent) => {\r\n if (document.pointerLockElement !== document.body)\r\n document.body.requestPointerLock();\r\n };\r\n\r\n // 更新模型动画\r\n private updateMixers(delta: number) {\r\n if (this.personMixer) this.personMixer.update(delta);\r\n }\r\n\r\n // BVH构建\r\n async createBVH(meshUrl: string = \"\"): Promise<void> {\r\n await this.initLoader(); // 初始化加载器\r\n\r\n const ensureAttributesMinimal = (\r\n geom: THREE.BufferGeometry\r\n ): THREE.BufferGeometry | null => {\r\n if (!geom.attributes.position) {\r\n // console.warn(\"跳过无 position 的几何体\", geom);\r\n return null;\r\n }\r\n if (!geom.attributes.normal) geom.computeVertexNormals();\r\n if (!geom.attributes.uv) {\r\n const count = geom.attributes.position.count;\r\n const dummyUV = new Float32Array(count * 2);\r\n geom.setAttribute(\"uv\", new THREE.BufferAttribute(dummyUV, 2));\r\n }\r\n return geom;\r\n };\r\n\r\n const collected: THREE.BufferGeometry[] = [];\r\n if (meshUrl == \"\") {\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n this.scene.traverse((c) => {\r\n const mesh = c as THREE.Mesh;\r\n if (mesh?.isMesh && mesh.geometry && c.name !== \"capsule\") {\r\n try {\r\n let geom = (mesh.geometry as THREE.BufferGeometry).clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n } catch (e) {\r\n console.warn(\"处理网格时出错:\", mesh, e);\r\n }\r\n }\r\n });\r\n\r\n if (!collected.length) {\r\n return;\r\n }\r\n\r\n // 统一属性集合\r\n type AttrMeta = { itemSize: number; arrayCtor: any; examples: number };\r\n const attrMap = new Map<string, AttrMeta>();\r\n const attrConflict = new Set<string>();\r\n\r\n for (const g of collected) {\r\n for (const name of Object.keys(g.attributes)) {\r\n const attr = g.attributes[name] as THREE.BufferAttribute;\r\n const ctor = (attr.array as any).constructor;\r\n const itemSize = attr.itemSize;\r\n if (!attrMap.has(name)) {\r\n attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });\r\n } else {\r\n const m = attrMap.get(name)!;\r\n if (m.itemSize !== itemSize || m.arrayCtor !== ctor)\r\n attrConflict.add(name);\r\n else m.examples++;\r\n }\r\n }\r\n }\r\n\r\n if (attrConflict.size) {\r\n for (const g of collected) {\r\n for (const name of Array.from(attrConflict)) {\r\n if (g.attributes[name]) g.deleteAttribute(name);\r\n }\r\n }\r\n for (const name of attrConflict) attrMap.delete(name);\r\n }\r\n\r\n const attrNames = Array.from(attrMap.keys());\r\n for (const g of collected) {\r\n const count = g.attributes.position.count;\r\n for (const name of attrNames) {\r\n if (!g.attributes[name]) {\r\n const meta = attrMap.get(name)!;\r\n const len = count * meta.itemSize;\r\n const array = new meta.arrayCtor(len);\r\n g.setAttribute(\r\n name,\r\n new THREE.BufferAttribute(array, meta.itemSize)\r\n );\r\n }\r\n }\r\n }\r\n } else {\r\n const gltf: GLTF = await this.loader.loadAsync(meshUrl, (xhr) => {});\r\n const mesh = gltf.scene.children[0] as THREE.Mesh;\r\n mesh.name = \"BVH加载模型\";\r\n\r\n // 推入几何体\r\n let geom = mesh.geometry.clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n }\r\n\r\n // 合并几何体\r\n const merged = BufferGeometryUtils.mergeGeometries(collected, false);\r\n if (!merged) {\r\n console.error(\"合并几何失败\");\r\n return;\r\n }\r\n\r\n // 构建bvh\r\n (merged as any).boundsTree = new MeshBVH(merged, {\r\n maxDepth: 100,\r\n });\r\n this.collider = new THREE.Mesh(\r\n merged,\r\n new THREE.MeshBasicMaterial({\r\n opacity: 0.5,\r\n transparent: true,\r\n wireframe: true,\r\n })\r\n );\r\n\r\n if (this.displayCollider) this.scene.add(this.collider);\r\n if (this.displayVisualizer) {\r\n if (this.visualizer) this.scene.remove(this.visualizer);\r\n this.visualizer = new MeshBVHHelper(this.collider, this.visualizeDepth);\r\n this.scene.add(this.visualizer);\r\n }\r\n this.boundingBoxMinY = (this.collider as any).geometry.boundingBox.min.y;\r\n // console.log(\"bvh加载模型成功\", this.collider);\r\n }\r\n}\r\n\r\n// 导出API\r\nexport function playerController() {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n const c = controllerInstance;\r\n return {\r\n init: (opts: PlayerControllerOptions, callback?: () => void) =>\r\n c.init(opts, callback),\r\n changeView: () => c.changeView(),\r\n reset: (pos?: THREE.Vector3) => c.reset(pos),\r\n update: (dt?: number) => c.update(dt),\r\n destroy: () => c.destroy(),\r\n };\r\n}\r\n\r\n// 打开所有事件\r\nexport function onAllEvent(): void {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n controllerInstance.onAllEvent();\r\n}\r\n\r\n// 关闭所有事件\r\nexport function offAllEvent(): void {\r\n if (!controllerInstance) return;\r\n controllerInstance.offAllEvent();\r\n}\r\n"],"mappings":"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","names":["angle","intersects"]}
|
package/dist/index.mjs
CHANGED
|
@@ -248,6 +248,7 @@ var PlayerController = class {
|
|
|
248
248
|
async init(opts, callback) {
|
|
249
249
|
this.scene = opts.scene;
|
|
250
250
|
this.camera = opts.camera;
|
|
251
|
+
this.camera.rotation.order = "YXZ";
|
|
251
252
|
this.controls = opts.controls;
|
|
252
253
|
this.playerModel = opts.playerModel;
|
|
253
254
|
this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);
|
|
@@ -256,13 +257,14 @@ var PlayerController = class {
|
|
|
256
257
|
this.visualizeDepth = 0 * s;
|
|
257
258
|
this.gravity = opts.playerModel.gravity ? opts.playerModel.gravity * s : -2400 * s;
|
|
258
259
|
this.jumpHeight = opts.playerModel.jumpHeight ? opts.playerModel.jumpHeight * s : 800 * s;
|
|
259
|
-
this.
|
|
260
|
+
this.originPlayerSpeed = opts.playerModel.speed ? opts.playerModel.speed * s : 400 * s;
|
|
261
|
+
this.playerSpeed = this.originPlayerSpeed;
|
|
260
262
|
this._camCollisionLerp = 0.18;
|
|
261
263
|
this._camEpsilon = 35 * s;
|
|
262
264
|
this._minCamDistance = opts.minCamDistance ? opts.minCamDistance * s : 100 * s;
|
|
263
265
|
this._maxCamDistance = opts.maxCamDistance ? opts.maxCamDistance * s : 440 * s;
|
|
264
266
|
this.orginMaxCamDistance = this._maxCamDistance;
|
|
265
|
-
await this.createBVH();
|
|
267
|
+
await this.createBVH(opts.colliderMeshUrl);
|
|
266
268
|
this.createPlayer();
|
|
267
269
|
await this.loadPersonGLB();
|
|
268
270
|
if (this.isFirstPerson && this.player) {
|
|
@@ -534,9 +536,9 @@ var PlayerController = class {
|
|
|
534
536
|
}
|
|
535
537
|
}
|
|
536
538
|
if (this.isFlying && this.fwdPressed) {
|
|
537
|
-
this.playerSpeed = this.shiftPressed ?
|
|
539
|
+
this.playerSpeed = this.shiftPressed ? this.originPlayerSpeed * 12 : this.originPlayerSpeed * 7;
|
|
538
540
|
} else {
|
|
539
|
-
this.playerSpeed = this.shiftPressed ?
|
|
541
|
+
this.playerSpeed = this.shiftPressed ? this.originPlayerSpeed * 2 : this.originPlayerSpeed;
|
|
540
542
|
}
|
|
541
543
|
this.moveDir.normalize().applyAxisAngle(this.upVector, angle);
|
|
542
544
|
this.player.position.addScaledVector(
|
|
@@ -725,7 +727,7 @@ var PlayerController = class {
|
|
|
725
727
|
}
|
|
726
728
|
}
|
|
727
729
|
}
|
|
728
|
-
// 重置
|
|
730
|
+
// 重置
|
|
729
731
|
reset(position) {
|
|
730
732
|
if (!this.player) return;
|
|
731
733
|
this.playerVelocity.set(0, 0, 0);
|
|
@@ -870,7 +872,9 @@ var PlayerController = class {
|
|
|
870
872
|
console.error("\u5408\u5E76\u51E0\u4F55\u5931\u8D25");
|
|
871
873
|
return;
|
|
872
874
|
}
|
|
873
|
-
merged.boundsTree = new MeshBVH(merged
|
|
875
|
+
merged.boundsTree = new MeshBVH(merged, {
|
|
876
|
+
maxDepth: 100
|
|
877
|
+
});
|
|
874
878
|
this.collider = new THREE.Mesh(
|
|
875
879
|
merged,
|
|
876
880
|
new THREE.MeshBasicMaterial({
|
|
@@ -886,7 +890,6 @@ var PlayerController = class {
|
|
|
886
890
|
this.scene.add(this.visualizer);
|
|
887
891
|
}
|
|
888
892
|
this.boundingBoxMinY = this.collider.geometry.boundingBox.min.y;
|
|
889
|
-
console.log("bvh\u52A0\u8F7D\u6A21\u578B\u6210\u529F", this.collider);
|
|
890
893
|
}
|
|
891
894
|
};
|
|
892
895
|
function playerController() {
|
|
@@ -895,14 +898,9 @@ function playerController() {
|
|
|
895
898
|
return {
|
|
896
899
|
init: (opts, callback) => c.init(opts, callback),
|
|
897
900
|
changeView: () => c.changeView(),
|
|
898
|
-
createBVH: (url = "") => c.createBVH(url),
|
|
899
|
-
createPlayer: () => c.createPlayer(),
|
|
900
901
|
reset: (pos) => c.reset(pos),
|
|
901
902
|
update: (dt) => c.update(dt),
|
|
902
|
-
destroy: () => c.destroy()
|
|
903
|
-
displayCollider: c.displayCollider,
|
|
904
|
-
displayPlayer: c.displayPlayer,
|
|
905
|
-
displayVisualizer: c.displayVisualizer
|
|
903
|
+
destroy: () => c.destroy()
|
|
906
904
|
};
|
|
907
905
|
}
|
|
908
906
|
function onAllEvent() {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/playerController.ts"],"sourcesContent":["import * as THREE from \"three\";\r\nimport { acceleratedRaycast, MeshBVH, MeshBVHHelper } from \"three-mesh-bvh\";\r\nimport type { GLTF } from \"three/examples/jsm/Addons.js\";\r\nimport { OrbitControls } from \"three/examples/jsm/controls/OrbitControls.js\";\r\nimport { RoundedBoxGeometry } from \"three/examples/jsm/geometries/RoundedBoxGeometry.js\";\r\nimport { DRACOLoader } from \"three/examples/jsm/loaders/DRACOLoader.js\";\r\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\r\nimport * as BufferGeometryUtils from \"three/examples/jsm/utils/BufferGeometryUtils.js\";\r\n\r\nTHREE.Mesh.prototype.raycast = acceleratedRaycast;\r\n\r\nlet controllerInstance: PlayerController | null = null; // 单例实例\r\nconst clock = new THREE.Clock();\r\n\r\nclass PlayerController {\r\n loader: GLTFLoader = new GLTFLoader();\r\n\r\n // 基本配置与参数\r\n scene!: THREE.Scene;\r\n camera!: THREE.PerspectiveCamera;\r\n controls!: OrbitControls;\r\n initPos!: THREE.Vector3;\r\n playerModel!: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n visualizeDepth!: number;\r\n gravity!: number;\r\n jumpHeight!: number;\r\n playerSpeed!: number;\r\n mouseSensity!: number;\r\n\r\n playerRadius: number = 45;\r\n playerHeight: number = 180;\r\n isFirstPerson: boolean = false;\r\n boundingBoxMinY: number = 0;\r\n // 测试参数\r\n displayPlayer: boolean = false;\r\n displayCollider: boolean = false;\r\n displayVisualizer: boolean = false;\r\n\r\n // 场景对象\r\n collider: THREE.Mesh | null = null;\r\n visualizer: MeshBVHHelper | null = null;\r\n player!: THREE.Mesh & { capsuleInfo?: any };\r\n person: THREE.Object3D | null = null;\r\n\r\n // 状态开关\r\n playerIsOnGround: boolean = false;\r\n isupdate: boolean = true;\r\n isFlying: boolean = false;\r\n\r\n // 输入状态\r\n fwdPressed: boolean = false;\r\n bkdPressed: boolean = false;\r\n lftPressed: boolean = false;\r\n rgtPressed: boolean = false;\r\n spacePressed: boolean = false;\r\n ctPressed: boolean = false;\r\n shiftPressed: boolean = false;\r\n\r\n // 第三人称\r\n _camCollisionLerp: number = 0.18; // 平滑系数\r\n _camEpsilon: number = 0.35; // 摄像机与障碍物之间的安全距离(米)\r\n _minCamDistance: number = 1.0; // 摄像机最小距离\r\n _maxCamDistance: number = 4.4; // 摄像机最大距离\r\n orginMaxCamDistance: number = 4.4;\r\n\r\n // 物理/运动\r\n playerVelocity = new THREE.Vector3(); // 玩家速度向量\r\n readonly upVector = new THREE.Vector3(0, 1, 0);\r\n\r\n // 临时复用向量/矩阵\r\n readonly tempVector = new THREE.Vector3();\r\n readonly tempVector2 = new THREE.Vector3();\r\n readonly tempBox = new THREE.Box3();\r\n readonly tempMat = new THREE.Matrix4();\r\n readonly tempSegment = new THREE.Line3();\r\n\r\n // 动画相关\r\n personMixer?: THREE.AnimationMixer;\r\n personActions?: Map<string, THREE.AnimationAction>;\r\n idleAction!: THREE.AnimationAction;\r\n walkAction!: THREE.AnimationAction;\r\n leftWalkAction!: THREE.AnimationAction;\r\n rightWalkAction!: THREE.AnimationAction;\r\n backwardAction!: THREE.AnimationAction;\r\n jumpAction!: THREE.AnimationAction;\r\n runAction!: THREE.AnimationAction;\r\n flyidleAction!: THREE.AnimationAction;\r\n flyAction!: THREE.AnimationAction;\r\n controlDroneAction!: THREE.AnimationAction;\r\n actionState!: THREE.AnimationAction;\r\n\r\n // 复用向量:用于相机朝向 / 移动\r\n readonly camDir = new THREE.Vector3();\r\n readonly moveDir = new THREE.Vector3();\r\n readonly targetQuat = new THREE.Quaternion();\r\n readonly targetMat = new THREE.Matrix4();\r\n readonly rotationSpeed = 10;\r\n readonly DIR_FWD = new THREE.Vector3(0, 0, -1);\r\n readonly DIR_BKD = new THREE.Vector3(0, 0, 1);\r\n readonly DIR_LFT = new THREE.Vector3(-1, 0, 0);\r\n readonly DIR_RGT = new THREE.Vector3(1, 0, 0);\r\n readonly DIR_UP = new THREE.Vector3(0, 1, 0);\r\n\r\n readonly _personToCam = new THREE.Vector3();\r\n\r\n readonly _originTmp = new THREE.Vector3();\r\n readonly _raycaster = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -1, 0)\r\n );\r\n readonly _raycasterPersonToCam = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3()\r\n );\r\n\r\n // 射线检测时只返回第一个碰撞\r\n constructor() {\r\n (this._raycaster as any).firstHitOnly = true;\r\n (this._raycasterPersonToCam as any).firstHitOnly = true;\r\n }\r\n\r\n // 初始化\r\n async init(\r\n opts: {\r\n scene: THREE.Scene;\r\n camera: THREE.PerspectiveCamera;\r\n controls: OrbitControls;\r\n playerModel: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n initPos?: THREE.Vector3;\r\n mouseSensity?: number;\r\n minCamDistance?: number;\r\n maxCamDistance?: number;\r\n },\r\n callback?: () => void\r\n ) {\r\n this.scene = opts.scene;\r\n this.camera = opts.camera;\r\n this.controls = opts.controls;\r\n this.playerModel = opts.playerModel;\r\n this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);\r\n this.mouseSensity = opts.mouseSensity ? opts.mouseSensity : 5;\r\n\r\n const s = this.playerModel.scale;\r\n this.visualizeDepth = 0 * s;\r\n this.gravity = opts.playerModel.gravity\r\n ? opts.playerModel.gravity * s\r\n : -2400 * s;\r\n this.jumpHeight = opts.playerModel.jumpHeight\r\n ? opts.playerModel.jumpHeight * s\r\n : 800 * s;\r\n this.playerSpeed = opts.playerModel.speed\r\n ? opts.playerModel.speed * s\r\n : 400 * s;\r\n\r\n this._camCollisionLerp = 0.18;\r\n this._camEpsilon = 35 * s;\r\n this._minCamDistance = opts.minCamDistance\r\n ? opts.minCamDistance * s\r\n : 100 * s;\r\n this._maxCamDistance = opts.maxCamDistance\r\n ? opts.maxCamDistance * s\r\n : 440 * s;\r\n this.orginMaxCamDistance = this._maxCamDistance;\r\n\r\n // 创建bvh\r\n await this.createBVH();\r\n\r\n // 创建玩家\r\n this.createPlayer();\r\n\r\n // 加载玩家模型\r\n await this.loadPersonGLB();\r\n\r\n // 等待资源加载完毕再设置摄像机\r\n if (this.isFirstPerson && this.player) {\r\n this.player.add(this.camera);\r\n }\r\n this.onAllEvent(); // 绑定事件\r\n this.setCameraPos();\r\n this.setControls();\r\n if (callback) callback();\r\n }\r\n\r\n // 第一/三视角切换\r\n changeView() {\r\n this.isFirstPerson = !this.isFirstPerson;\r\n if (this.isFirstPerson) {\r\n this.player.attach(this.camera);\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n this.camera.rotation.set(0, Math.PI, 0);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n } else {\r\n this.scene.attach(this.camera);\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n this.controls.target.copy(worldPos);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n }\r\n }\r\n\r\n // 摄像机/控制器设置\r\n setCameraPos() {\r\n if (this.isFirstPerson) {\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n } else {\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n }\r\n this.camera.updateProjectionMatrix();\r\n }\r\n\r\n // 设置控制器\r\n setControls() {\r\n this.controls.enabled = false;\r\n this.controls.maxPolarAngle = Math.PI * (300 / 360);\r\n }\r\n\r\n // 重置控制器\r\n resetControls() {\r\n if (!this.controls) return;\r\n this.controls.enabled = true;\r\n this.controls.enablePan = true;\r\n this.controls.maxPolarAngle = Math.PI / 2;\r\n this.controls.rotateSpeed = 1;\r\n this.controls.enableZoom = true;\r\n this.controls.mouseButtons = {\r\n LEFT: 0,\r\n MIDDLE: 1,\r\n RIGHT: 2,\r\n };\r\n }\r\n\r\n // 初始化加载器\r\n async initLoader() {\r\n const dracoLoader = new DRACOLoader();\r\n dracoLoader.setDecoderPath(\r\n \"https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/\"\r\n );\r\n dracoLoader.setDecoderConfig({ type: \"js\" });\r\n this.loader.setDRACOLoader(dracoLoader);\r\n }\r\n\r\n // 人物与动画加载\r\n async loadPersonGLB() {\r\n try {\r\n const gltf: GLTF = await this.loader.loadAsync(this.playerModel.url);\r\n this.person = gltf.scene;\r\n const sc = this.playerModel.scale;\r\n const h = this.playerHeight * sc;\r\n this.person.scale.set(sc, sc, sc);\r\n this.person.position.set(0, -h * 0.75, 0);\r\n this.person.traverse((child: any) => {\r\n if (child.isMesh) {\r\n child.castShadow = true;\r\n\r\n const mat = child.material;\r\n if (!mat) return;\r\n\r\n // 处理多材质的情况\r\n const mats = Array.isArray(mat) ? mat : [mat];\r\n\r\n mats.forEach((m) => {\r\n // console.log(\"mat\", m);\r\n // m.envMap = this.scene.background;\r\n // m.envMapIntensity = 1.0;\r\n });\r\n }\r\n });\r\n this.player.add(this.person);\r\n this.reset();\r\n\r\n // 创建人物 mixer 与 actions\r\n this.personMixer = new THREE.AnimationMixer(this.person);\r\n const animations = gltf.animations ?? [];\r\n this.personActions = new Map<string, THREE.AnimationAction>();\r\n // 取出动作并注册到 map\r\n const findClip = (name: string) =>\r\n animations.find((a: any) => a.name === name);\r\n const regs: [string, string][] = [\r\n [this.playerModel.idleAnim, \"idle\"],\r\n [this.playerModel.walkAnim, \"walking\"],\r\n [\r\n this.playerModel.leftWalkAnim || this.playerModel.walkAnim,\r\n \"left_walking\",\r\n ],\r\n [\r\n this.playerModel.rightWalkAnim || this.playerModel.walkAnim,\r\n \"right_walking\",\r\n ],\r\n [\r\n this.playerModel.backwardAnim || this.playerModel.walkAnim,\r\n \"walking_backward\",\r\n ],\r\n [this.playerModel.jumpAnim, \"jumping\"],\r\n [this.playerModel.runAnim, \"running\"],\r\n [this.playerModel.flyIdleAnim || this.playerModel.idleAnim, \"flyidle\"],\r\n [this.playerModel.flyAnim || this.playerModel.idleAnim, \"flying\"],\r\n ];\r\n\r\n // 注册动作并设置循环模式\r\n for (const [key, clipName] of regs) {\r\n const clip = findClip(key);\r\n if (!clip) continue;\r\n const action = this.personMixer.clipAction(clip);\r\n\r\n if (clipName === \"jumping\") {\r\n action.setLoop(THREE.LoopOnce, 1); // 播放一次\r\n action.clampWhenFinished = true;\r\n action.setEffectiveTimeScale(1.2); // 播放速度\r\n } else {\r\n action.setLoop(THREE.LoopRepeat, Infinity); // 循环播放\r\n action.clampWhenFinished = false;\r\n action.setEffectiveTimeScale(1);\r\n }\r\n\r\n action.enabled = true; // 激活\r\n action.setEffectiveWeight(0); // 初始权重为0\r\n this.personActions.set(clipName, action);\r\n }\r\n\r\n // 把actions激活\r\n this.idleAction = this.personActions.get(\"idle\")!;\r\n this.walkAction = this.personActions.get(\"walking\")!;\r\n this.leftWalkAction = this.personActions.get(\"left_walking\")!;\r\n this.rightWalkAction = this.personActions.get(\"right_walking\")!;\r\n this.backwardAction = this.personActions.get(\"walking_backward\")!;\r\n this.jumpAction = this.personActions.get(\"jumping\")!;\r\n this.runAction = this.personActions.get(\"running\")!;\r\n this.flyidleAction = this.personActions.get(\"flyidle\")!;\r\n this.flyAction = this.personActions.get(\"flying\")!;\r\n\r\n // 激活空闲动作\r\n this.idleAction.setEffectiveWeight(1);\r\n this.idleAction.play();\r\n this.actionState = this.idleAction;\r\n\r\n this.personMixer.addEventListener(\"finished\", (ev: any) => {\r\n const finishedAction: THREE.AnimationAction = ev.action;\r\n\r\n if (finishedAction === this.jumpAction) {\r\n // jump 播放结束后的逻辑\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) this.playPersonAnimationByName(\"running\");\r\n else this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n if (this.rgtPressed || this.lftPressed) {\r\n this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"idle\");\r\n }\r\n });\r\n } catch (error) {}\r\n }\r\n\r\n // 平滑切换人物动画\r\n playPersonAnimationByName(name: string, fade = 0.18) {\r\n // console.log(\"播放动画\", name);\r\n if (!this.personActions) return;\r\n if (this.ctPressed) return;\r\n\r\n const next = this.personActions.get(name);\r\n if (!next) return;\r\n\r\n // 如果是同一个action,直接返回\r\n if (this.actionState === next) return;\r\n\r\n const prev = this.actionState;\r\n\r\n // 对于一次性动作先reset()\r\n next.reset();\r\n next.setEffectiveWeight(1);\r\n next.play();\r\n\r\n if (prev && prev !== next) {\r\n // 让 prev 淡出,next 淡入\r\n prev.fadeOut(fade);\r\n next.fadeIn(fade);\r\n } else {\r\n // 时直接淡入\r\n next.fadeIn(fade);\r\n }\r\n\r\n this.actionState = next;\r\n }\r\n\r\n // 创建玩家胶囊体\r\n createPlayer() {\r\n const material = new THREE.MeshStandardMaterial({\r\n color: new THREE.Color(1, 0, 0),\r\n shadowSide: THREE.DoubleSide,\r\n depthTest: false,\r\n });\r\n material.transparent = true;\r\n material.opacity = this.displayPlayer ? 0.5 : 0;\r\n material.wireframe = true;\r\n material.depthWrite = false;\r\n\r\n const r = this.playerRadius * this.playerModel.scale;\r\n const h = this.playerHeight * this.playerModel.scale;\r\n this.player = new THREE.Mesh(\r\n new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75),\r\n material\r\n ) as typeof this.player;\r\n\r\n this.player.geometry.translate(0, -h * 0.25, 0);\r\n this.player.capsuleInfo = {\r\n radius: r,\r\n segment: new THREE.Line3(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -h * 0.5, 0)\r\n ),\r\n };\r\n\r\n this.player.name = \"capsule\";\r\n this.scene.add(this.player);\r\n this.reset();\r\n }\r\n\r\n // 获取法线与Y轴的夹角\r\n getAngleWithYAxis(normal: { x: number; y: number; z: number }): number {\r\n // Y轴正方向向量\r\n const yAxis = { x: 0, y: 1, z: 0 };\r\n\r\n // 向量点积\r\n const dotProduct =\r\n normal.x * yAxis.x + normal.y * yAxis.y + normal.z * yAxis.z;\r\n\r\n // 向量模长\r\n const normalMagnitude = Math.sqrt(\r\n normal.x * normal.x + normal.y * normal.y + normal.z * normal.z\r\n );\r\n const yAxisMagnitude = 1; // Y轴单位向量长度为1\r\n\r\n // 计算夹角余弦值\r\n const cosTheta = dotProduct / (normalMagnitude * yAxisMagnitude);\r\n\r\n // 返回夹角(弧度)\r\n return Math.acos(cosTheta);\r\n }\r\n\r\n // 每帧更新\r\n async update(delta: number = clock.getDelta()) {\r\n if (!this.isupdate || !this.player || !this.collider) return;\r\n delta = Math.min(delta, 1 / 30);\r\n\r\n if (!this.isFlying)\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.updateMixers(delta);\r\n this.camera.getWorldDirection(this.camDir);\r\n let angle = Math.atan2(this.camDir.z, this.camDir.x) + Math.PI / 2;\r\n angle = 2 * Math.PI - angle;\r\n\r\n this.moveDir.set(0, 0, 0);\r\n if (this.fwdPressed) this.moveDir.add(this.DIR_FWD);\r\n if (this.bkdPressed) this.moveDir.add(this.DIR_BKD);\r\n if (this.lftPressed) this.moveDir.add(this.DIR_LFT);\r\n if (this.rgtPressed) this.moveDir.add(this.DIR_RGT);\r\n if (this.isFlying) {\r\n if (this.fwdPressed) {\r\n this.moveDir.y = this.camDir.y;\r\n } else {\r\n this.moveDir.y = 0;\r\n }\r\n if (this.spacePressed) {\r\n this.moveDir.add(this.DIR_UP);\r\n }\r\n }\r\n // 设置速度\r\n if (this.isFlying && this.fwdPressed) {\r\n this.playerSpeed = this.shiftPressed\r\n ? 4000 * this.playerModel.scale\r\n : 3000 * this.playerModel.scale;\r\n } else {\r\n this.playerSpeed = this.shiftPressed\r\n ? 900 * this.playerModel.scale\r\n : 400 * this.playerModel.scale;\r\n }\r\n this.moveDir.normalize().applyAxisAngle(this.upVector, angle);\r\n this.player.position.addScaledVector(\r\n this.moveDir,\r\n this.playerSpeed * delta\r\n );\r\n\r\n // 向下射线检测地面高度\r\n let playerDistanceFromGround = Infinity;\r\n this._originTmp.set(\r\n this.player.position.x,\r\n this.player.position.y,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n playerDistanceFromGround = this.player.position.y - intersects[0].point.y;\r\n const normal = intersects[0].normal as THREE.Vector3;\r\n const angle = (this.getAngleWithYAxis(normal) * 180) / Math.PI;\r\n const maxH = this.playerHeight * this.playerModel.scale * 0.9; // 坡度高度阈值\r\n const h = this.playerHeight * this.playerModel.scale * 0.75; // 正常高度\r\n const minH = this.playerHeight * this.playerModel.scale * 0.7; // 最小高度\r\n\r\n if (this.isFlying) {\r\n } else {\r\n if (playerDistanceFromGround > maxH) {\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = false;\r\n } else if (\r\n playerDistanceFromGround > h &&\r\n playerDistanceFromGround < maxH\r\n ) {\r\n if (angle >= 0 && angle < 5) {\r\n // 平地\r\n // this.player.position.y = intersects[0].point.y + h;\r\n // this.playerVelocity.set(0, 0, 0);\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = true;\r\n } else {\r\n // 坡地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n }\r\n } else if (\r\n playerDistanceFromGround > minH &&\r\n playerDistanceFromGround < h\r\n ) {\r\n // 误差范围内 在平地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n } else if (playerDistanceFromGround < minH) {\r\n // 强行拉回\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.set(\r\n this.player.position.x,\r\n intersects[0].point.y + h,\r\n this.player.position.z\r\n );\r\n this.playerIsOnGround = true;\r\n }\r\n }\r\n }\r\n\r\n // 更新玩家矩阵\r\n this.player.updateMatrixWorld();\r\n // 碰撞检测\r\n const capsuleInfo = this.player.capsuleInfo;\r\n this.tempBox.makeEmpty();\r\n this.tempMat.copy(this.collider!.matrixWorld).invert();\r\n this.tempSegment.copy(capsuleInfo.segment);\r\n this.tempSegment.start\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n this.tempSegment.end\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n\r\n this.tempBox.expandByPoint(this.tempSegment.start);\r\n this.tempBox.expandByPoint(this.tempSegment.end);\r\n this.tempBox.expandByScalar(capsuleInfo.radius);\r\n\r\n const bvh = this.collider?.geometry as any;\r\n bvh?.boundsTree?.shapecast({\r\n // 检测包围盒碰撞\r\n intersectsBounds: (box: THREE.Box3) => box.intersectsBox(this.tempBox),\r\n // 检测三角形碰撞\r\n intersectsTriangle: (tri: any) => {\r\n const triPoint = this.tempVector;\r\n const capsulePoint = this.tempVector2;\r\n const distance = tri.closestPointToSegment(\r\n this.tempSegment,\r\n triPoint,\r\n capsulePoint\r\n );\r\n // 距离小于人物半径,发生碰撞\r\n if (distance < capsuleInfo.radius) {\r\n const depth = capsuleInfo.radius - distance;\r\n const direction = capsulePoint.sub(triPoint).normalize();\r\n this.tempSegment.start.addScaledVector(direction, depth);\r\n this.tempSegment.end.addScaledVector(direction, depth);\r\n }\r\n },\r\n });\r\n\r\n // 设置玩家位置\r\n const newPosition = this.tempVector\r\n .copy(this.tempSegment.start)\r\n .applyMatrix4(this.collider!.matrixWorld);\r\n const deltaVector = this.tempVector2.subVectors(\r\n newPosition,\r\n this.player.position\r\n );\r\n // 应用位移\r\n const offset = Math.max(0, deltaVector.length() - 1e-5);\r\n deltaVector.normalize().multiplyScalar(offset);\r\n this.player.position.add(deltaVector);\r\n\r\n // 第三人称-朝向\r\n if (!this.isFirstPerson && this.moveDir.lengthSq() > 0 && !this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position.clone().add(this.moveDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 飞行\r\n if (this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position\r\n .clone()\r\n .add(this.fwdPressed ? this.moveDir : this.camDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 第三人称-相机跟随\r\n if (!this.isFirstPerson) {\r\n const lookTarget = this.player.position.clone();\r\n lookTarget.y += 30 * this.playerModel.scale;\r\n this.camera.position.sub(this.controls.target); // 减去控制器向量\r\n this.controls.target.copy(lookTarget); // 设置控制器目标\r\n this.camera.position.add(lookTarget); // 设置相机位置\r\n this.controls.update(); // 更新控制器\r\n\r\n // 当视线被遮挡时判断\r\n this._personToCam.subVectors(this.camera.position, this.player.position); // 计算从player指向camera的向量(camera - player)\r\n const origin = this.player.position\r\n .clone()\r\n .add(new THREE.Vector3(0, 0, 0)); // 射线起点\r\n const direction = this._personToCam.clone().normalize(); // 方向\r\n const desiredDist = this._personToCam.length(); // 与期望距离\r\n this._raycasterPersonToCam.set(origin, direction);\r\n this._raycasterPersonToCam.far = desiredDist;\r\n\r\n // 做相交检测\r\n const intersects = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 相机拉近\r\n const hit = intersects[0]; // 找到第一个命中\r\n const safeDist = Math.max(\r\n hit.distance - this._camEpsilon,\r\n this._minCamDistance\r\n ); // 计算安全距离(hit.distance是从origin到碰撞点的距离)\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist)); // 目标相机位置 = origin + direction * safeDist\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp); // 平滑移动相机到targetCamPos\r\n } else {\r\n // 相机恢复\r\n // const dis = this.player.position.distanceTo(this.camera.position); // 计算当前人物到相机距离\r\n this._raycasterPersonToCam.far = this._maxCamDistance;\r\n // 检查预设相机位置是否有遮挡\r\n const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n // 恢复相机\r\n let safeDist = this._maxCamDistance;\r\n if (intersectsMaxDis.length) {\r\n const hitMax = intersectsMaxDis[0]; // 找到第一个命中\r\n safeDist = hitMax.distance - this._camEpsilon;\r\n }\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist));\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp);\r\n }\r\n }\r\n\r\n // 掉出场景重置\r\n if (this.player.position.y < this.boundingBoxMinY - 1) {\r\n // 检测当前位置与碰撞体是否相交\r\n this._originTmp.set(\r\n this.player.position.x,\r\n 10000,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 出现碰撞 说明玩家为bug意外掉落\r\n console.log(\"玩家为bug意外掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n intersects[0].point.y + 5,\r\n this.player.position.z\r\n )\r\n );\r\n } else {\r\n // 无碰撞 正常掉落\r\n console.log(\"玩家正常掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n this.player.position.y + 15,\r\n this.player.position.z\r\n )\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 重置 / 销毁\r\n reset(position?: THREE.Vector3) {\r\n if (!this.player) return;\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.copy(position ? position : this.initPos);\r\n }\r\n\r\n // 销毁\r\n destroy() {\r\n this.offAllEvent();\r\n if (this.player) {\r\n this.player.remove(this.camera);\r\n this.scene.remove(this.player);\r\n }\r\n (this.player as any) = null;\r\n if (this.person) {\r\n this.scene.remove(this.person);\r\n this.person = null;\r\n }\r\n\r\n this.resetControls();\r\n\r\n // 清理 BVH 可视化\r\n if (this.visualizer) {\r\n this.scene.remove(this.visualizer);\r\n this.visualizer = null;\r\n }\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n\r\n controllerInstance = null;\r\n }\r\n\r\n // 事件绑定\r\n onAllEvent() {\r\n this.isupdate = true;\r\n document.body.requestPointerLock();\r\n window.addEventListener(\"keydown\", this._boundOnKeydown);\r\n window.addEventListener(\"keyup\", this._boundOnKeyup);\r\n window.addEventListener(\"mousemove\", this._mouseMove);\r\n window.addEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 事件解绑\r\n offAllEvent() {\r\n this.isupdate = false;\r\n document.exitPointerLock();\r\n window.removeEventListener(\"keydown\", this._boundOnKeydown);\r\n window.removeEventListener(\"keyup\", this._boundOnKeyup);\r\n window.removeEventListener(\"mousemove\", this._mouseMove);\r\n window.removeEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 键盘按下事件\r\n private _boundOnKeydown = async (e: KeyboardEvent) => {\r\n if (\r\n e.ctrlKey &&\r\n (e.code === \"KeyW\" ||\r\n e.code === \"KeyA\" ||\r\n e.code === \"KeyS\" ||\r\n e.code === \"KeyD\")\r\n ) {\r\n e.preventDefault();\r\n }\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = true;\r\n if (!this.playerIsOnGround || this.isFlying) return;\r\n this.playPersonAnimationByName(\"jumping\");\r\n this.playerVelocity.y = this.jumpHeight;\r\n this.playerIsOnGround = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = true;\r\n break;\r\n case \"KeyV\":\r\n this.changeView();\r\n break;\r\n case \"KeyF\":\r\n this.isFlying = !this.isFlying;\r\n this.setAnimationByPressed();\r\n break;\r\n }\r\n };\r\n\r\n // 键盘抬起事件\r\n private _boundOnKeyup = (e: KeyboardEvent) => {\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = false;\r\n break;\r\n }\r\n };\r\n\r\n // 根据按键设置人物动画\r\n setAnimationByPressed = () => {\r\n this._maxCamDistance = this.orginMaxCamDistance;\r\n if (this.isFlying) {\r\n if (!this.fwdPressed) {\r\n this.playPersonAnimationByName(\"flyidle\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"flying\");\r\n // this._maxCamDistance = 700 * this.playerModel.scale;\r\n this._maxCamDistance = this.orginMaxCamDistance * 2;\r\n return;\r\n }\r\n\r\n if (this.playerIsOnGround) {\r\n if (\r\n !this.fwdPressed &&\r\n !this.bkdPressed &&\r\n !this.lftPressed &&\r\n !this.rgtPressed\r\n ) {\r\n this.playPersonAnimationByName(\"idle\");\r\n return;\r\n }\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第三人称下动画统一使用 前进 动画\r\n if (\r\n !this.isFirstPerson &&\r\n (this.lftPressed || this.rgtPressed || this.bkdPressed)\r\n ) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第一人称下根据方向播放不同动画\r\n if (this.lftPressed) {\r\n this.playPersonAnimationByName(\"left_walking\");\r\n return;\r\n }\r\n if (this.rgtPressed) {\r\n this.playPersonAnimationByName(\"right_walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n } else {\r\n this.playPersonAnimationByName(\"jumping\");\r\n }\r\n };\r\n\r\n // 鼠标移动事件\r\n private _mouseMove = (e: MouseEvent) => {\r\n // 记录状态\r\n if (document.pointerLockElement !== document.body) return;\r\n if (this.isFirstPerson) {\r\n const yaw = -e.movementX * 0.0001 * this.mouseSensity;\r\n const pitch = -e.movementY * 0.0001 * this.mouseSensity;\r\n this.player.rotateY(yaw);\r\n this.camera.rotation.x = THREE.MathUtils.clamp(\r\n this.camera.rotation.x + pitch,\r\n -1.3,\r\n 1.4\r\n );\r\n } else {\r\n const sensitivity = 0.0001 * this.mouseSensity;\r\n const deltaX = -e.movementX * sensitivity;\r\n const deltaY = -e.movementY * sensitivity;\r\n // 获取目标点\r\n const target = this.player.position.clone();\r\n // 计算相机到目标的距离\r\n const distance = this.camera.position.distanceTo(target);\r\n // 计算当前角度\r\n const currentPosition = this.camera.position.clone().sub(target);\r\n let theta = Math.atan2(currentPosition.x, currentPosition.z);\r\n let phi = Math.acos(currentPosition.y / distance);\r\n // 应用旋转\r\n theta += deltaX;\r\n phi += deltaY;\r\n // 限制phi角度\r\n phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));\r\n // 计算新的相机位置\r\n const newX = distance * Math.sin(phi) * Math.sin(theta);\r\n const newY = distance * Math.cos(phi);\r\n const newZ = distance * Math.sin(phi) * Math.cos(theta);\r\n\r\n this.camera.position.set(\r\n target.x + newX,\r\n target.y + newY,\r\n target.z + newZ\r\n );\r\n this.camera.lookAt(target);\r\n }\r\n };\r\n\r\n private _mouseClick = (e: MouseEvent) => {\r\n if (document.pointerLockElement !== document.body)\r\n document.body.requestPointerLock();\r\n };\r\n\r\n // 更新模型动画\r\n private updateMixers(delta: number) {\r\n if (this.personMixer) this.personMixer.update(delta);\r\n }\r\n\r\n // BVH构建\r\n async createBVH(meshUrl: string = \"\"): Promise<void> {\r\n await this.initLoader(); // 初始化加载器\r\n\r\n const ensureAttributesMinimal = (\r\n geom: THREE.BufferGeometry\r\n ): THREE.BufferGeometry | null => {\r\n if (!geom.attributes.position) {\r\n // console.warn(\"跳过无 position 的几何体\", geom);\r\n return null;\r\n }\r\n if (!geom.attributes.normal) geom.computeVertexNormals();\r\n if (!geom.attributes.uv) {\r\n const count = geom.attributes.position.count;\r\n const dummyUV = new Float32Array(count * 2);\r\n geom.setAttribute(\"uv\", new THREE.BufferAttribute(dummyUV, 2));\r\n }\r\n return geom;\r\n };\r\n\r\n const collected: THREE.BufferGeometry[] = [];\r\n if (meshUrl == \"\") {\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n this.scene.traverse((c) => {\r\n const mesh = c as THREE.Mesh;\r\n if (mesh?.isMesh && mesh.geometry && c.name !== \"capsule\") {\r\n try {\r\n let geom = (mesh.geometry as THREE.BufferGeometry).clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n } catch (e) {\r\n console.warn(\"处理网格时出错:\", mesh, e);\r\n }\r\n }\r\n });\r\n\r\n if (!collected.length) {\r\n return;\r\n }\r\n\r\n // 统一属性集合\r\n type AttrMeta = { itemSize: number; arrayCtor: any; examples: number };\r\n const attrMap = new Map<string, AttrMeta>();\r\n const attrConflict = new Set<string>();\r\n\r\n for (const g of collected) {\r\n for (const name of Object.keys(g.attributes)) {\r\n const attr = g.attributes[name] as THREE.BufferAttribute;\r\n const ctor = (attr.array as any).constructor;\r\n const itemSize = attr.itemSize;\r\n if (!attrMap.has(name)) {\r\n attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });\r\n } else {\r\n const m = attrMap.get(name)!;\r\n if (m.itemSize !== itemSize || m.arrayCtor !== ctor)\r\n attrConflict.add(name);\r\n else m.examples++;\r\n }\r\n }\r\n }\r\n\r\n if (attrConflict.size) {\r\n for (const g of collected) {\r\n for (const name of Array.from(attrConflict)) {\r\n if (g.attributes[name]) g.deleteAttribute(name);\r\n }\r\n }\r\n for (const name of attrConflict) attrMap.delete(name);\r\n }\r\n\r\n const attrNames = Array.from(attrMap.keys());\r\n for (const g of collected) {\r\n const count = g.attributes.position.count;\r\n for (const name of attrNames) {\r\n if (!g.attributes[name]) {\r\n const meta = attrMap.get(name)!;\r\n const len = count * meta.itemSize;\r\n const array = new meta.arrayCtor(len);\r\n g.setAttribute(\r\n name,\r\n new THREE.BufferAttribute(array, meta.itemSize)\r\n );\r\n }\r\n }\r\n }\r\n } else {\r\n const gltf: GLTF = await this.loader.loadAsync(meshUrl, (xhr) => {});\r\n const mesh = gltf.scene.children[0] as THREE.Mesh;\r\n mesh.name = \"BVH加载模型\";\r\n\r\n // 推入几何体\r\n let geom = mesh.geometry.clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n }\r\n\r\n // 合并几何体\r\n const merged = BufferGeometryUtils.mergeGeometries(collected, false);\r\n if (!merged) {\r\n console.error(\"合并几何失败\");\r\n return;\r\n }\r\n\r\n // 构建bvh\r\n (merged as any).boundsTree = new MeshBVH(merged);\r\n this.collider = new THREE.Mesh(\r\n merged,\r\n new THREE.MeshBasicMaterial({\r\n opacity: 0.5,\r\n transparent: true,\r\n wireframe: true,\r\n })\r\n );\r\n\r\n if (this.displayCollider) this.scene.add(this.collider);\r\n if (this.displayVisualizer) {\r\n if (this.visualizer) this.scene.remove(this.visualizer);\r\n this.visualizer = new MeshBVHHelper(this.collider, this.visualizeDepth);\r\n this.scene.add(this.visualizer);\r\n }\r\n this.boundingBoxMinY = (this.collider as any).geometry.boundingBox.min.y;\r\n console.log(\"bvh加载模型成功\", this.collider);\r\n }\r\n}\r\n\r\n// 导出API\r\nexport function playerController() {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n const c = controllerInstance;\r\n return {\r\n init: (\r\n opts: {\r\n scene: THREE.Scene;\r\n camera: THREE.PerspectiveCamera;\r\n controls: OrbitControls;\r\n playerModel: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n initPos?: THREE.Vector3;\r\n mouseSensity?: number;\r\n minCamDistance?: number;\r\n maxCamDistance?: number;\r\n },\r\n callback?: () => void\r\n ) => c.init(opts, callback),\r\n changeView: () => c.changeView(),\r\n createBVH: (url: string = \"\") => c.createBVH(url),\r\n createPlayer: () => c.createPlayer(),\r\n reset: (pos?: THREE.Vector3) => c.reset(pos),\r\n update: (dt?: number) => c.update(dt),\r\n destroy: () => c.destroy(),\r\n displayCollider: c.displayCollider,\r\n displayPlayer: c.displayPlayer,\r\n displayVisualizer: c.displayVisualizer,\r\n };\r\n}\r\n\r\n// 打开所有事件\r\nexport function onAllEvent(): void {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n controllerInstance.onAllEvent();\r\n}\r\n\r\n// 关闭所有事件\r\nexport function offAllEvent(): void {\r\n if (!controllerInstance) return;\r\n controllerInstance.offAllEvent();\r\n}\r\n"],"mappings":"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","names":["angle","intersects"]}
|
|
1
|
+
{"version":3,"sources":["../src/playerController.ts"],"sourcesContent":["import * as THREE from \"three\";\r\nimport { acceleratedRaycast, MeshBVH, MeshBVHHelper } from \"three-mesh-bvh\";\r\nimport type { GLTF } from \"three/examples/jsm/Addons.js\";\r\nimport { OrbitControls } from \"three/examples/jsm/controls/OrbitControls.js\";\r\nimport { RoundedBoxGeometry } from \"three/examples/jsm/geometries/RoundedBoxGeometry.js\";\r\nimport { DRACOLoader } from \"three/examples/jsm/loaders/DRACOLoader.js\";\r\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\r\nimport * as BufferGeometryUtils from \"three/examples/jsm/utils/BufferGeometryUtils.js\";\r\n\r\nTHREE.Mesh.prototype.raycast = acceleratedRaycast;\r\n\r\nlet controllerInstance: PlayerController | null = null;\r\nconst clock = new THREE.Clock();\r\n\r\ntype PlayerControllerOptions = {\r\n scene: THREE.Scene;\r\n camera: THREE.PerspectiveCamera;\r\n controls: OrbitControls;\r\n playerModel: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n initPos?: THREE.Vector3;\r\n mouseSensity?: number;\r\n minCamDistance?: number;\r\n maxCamDistance?: number;\r\n colliderMeshUrl?: string;\r\n};\r\n\r\nclass PlayerController {\r\n loader: GLTFLoader = new GLTFLoader();\r\n // 基本配置与参数\r\n scene!: THREE.Scene;\r\n camera!: THREE.PerspectiveCamera;\r\n controls!: OrbitControls;\r\n initPos!: THREE.Vector3;\r\n playerModel!: {\r\n url: string;\r\n idleAnim: string;\r\n walkAnim: string;\r\n runAnim: string;\r\n jumpAnim: string;\r\n leftWalkAnim?: string;\r\n rightWalkAnim?: string;\r\n backwardAnim?: string;\r\n flyAnim?: string;\r\n flyIdleAnim?: string;\r\n scale: number;\r\n gravity?: number;\r\n jumpHeight?: number;\r\n speed?: number;\r\n };\r\n visualizeDepth!: number;\r\n gravity!: number;\r\n jumpHeight!: number;\r\n playerSpeed!: number;\r\n mouseSensity!: number;\r\n originPlayerSpeed!: number;\r\n colliderMeshUrl!: string;\r\n\r\n playerRadius: number = 45;\r\n playerHeight: number = 180;\r\n isFirstPerson: boolean = false;\r\n boundingBoxMinY: number = 0;\r\n // 测试参数\r\n displayPlayer: boolean = false;\r\n displayCollider: boolean = false;\r\n displayVisualizer: boolean = false;\r\n\r\n // 场景对象\r\n collider: THREE.Mesh | null = null;\r\n visualizer: MeshBVHHelper | null = null;\r\n player!: THREE.Mesh & { capsuleInfo?: any };\r\n person: THREE.Object3D | null = null;\r\n\r\n // 状态开关\r\n playerIsOnGround: boolean = false;\r\n isupdate: boolean = true;\r\n isFlying: boolean = false;\r\n\r\n // 输入状态\r\n fwdPressed: boolean = false;\r\n bkdPressed: boolean = false;\r\n lftPressed: boolean = false;\r\n rgtPressed: boolean = false;\r\n spacePressed: boolean = false;\r\n ctPressed: boolean = false;\r\n shiftPressed: boolean = false;\r\n\r\n // 第三人称\r\n _camCollisionLerp: number = 0.18; // 平滑系数\r\n _camEpsilon: number = 0.35; // 摄像机与障碍物之间的安全距离(米)\r\n _minCamDistance: number = 1.0; // 摄像机最小距离\r\n _maxCamDistance: number = 4.4; // 摄像机最大距离\r\n orginMaxCamDistance: number = 4.4;\r\n\r\n // 物理/运动\r\n playerVelocity = new THREE.Vector3(); // 玩家速度向量\r\n readonly upVector = new THREE.Vector3(0, 1, 0);\r\n\r\n // 临时复用向量/矩阵\r\n readonly tempVector = new THREE.Vector3();\r\n readonly tempVector2 = new THREE.Vector3();\r\n readonly tempBox = new THREE.Box3();\r\n readonly tempMat = new THREE.Matrix4();\r\n readonly tempSegment = new THREE.Line3();\r\n\r\n // 动画相关\r\n personMixer?: THREE.AnimationMixer;\r\n personActions?: Map<string, THREE.AnimationAction>;\r\n idleAction!: THREE.AnimationAction;\r\n walkAction!: THREE.AnimationAction;\r\n leftWalkAction!: THREE.AnimationAction;\r\n rightWalkAction!: THREE.AnimationAction;\r\n backwardAction!: THREE.AnimationAction;\r\n jumpAction!: THREE.AnimationAction;\r\n runAction!: THREE.AnimationAction;\r\n flyidleAction!: THREE.AnimationAction;\r\n flyAction!: THREE.AnimationAction;\r\n controlDroneAction!: THREE.AnimationAction;\r\n actionState!: THREE.AnimationAction;\r\n\r\n // 复用向量:用于相机朝向 / 移动\r\n readonly camDir = new THREE.Vector3();\r\n readonly moveDir = new THREE.Vector3();\r\n readonly targetQuat = new THREE.Quaternion();\r\n readonly targetMat = new THREE.Matrix4();\r\n readonly rotationSpeed = 10;\r\n readonly DIR_FWD = new THREE.Vector3(0, 0, -1);\r\n readonly DIR_BKD = new THREE.Vector3(0, 0, 1);\r\n readonly DIR_LFT = new THREE.Vector3(-1, 0, 0);\r\n readonly DIR_RGT = new THREE.Vector3(1, 0, 0);\r\n readonly DIR_UP = new THREE.Vector3(0, 1, 0);\r\n\r\n readonly _personToCam = new THREE.Vector3();\r\n\r\n readonly _originTmp = new THREE.Vector3();\r\n readonly _raycaster = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -1, 0)\r\n );\r\n readonly _raycasterPersonToCam = new THREE.Raycaster(\r\n new THREE.Vector3(),\r\n new THREE.Vector3()\r\n );\r\n\r\n // 射线检测时只返回第一个碰撞\r\n constructor() {\r\n (this._raycaster as any).firstHitOnly = true;\r\n (this._raycasterPersonToCam as any).firstHitOnly = true;\r\n }\r\n\r\n // 初始化\r\n async init(opts: PlayerControllerOptions, callback?: () => void) {\r\n this.scene = opts.scene;\r\n this.camera = opts.camera;\r\n this.camera.rotation.order = \"YXZ\";\r\n this.controls = opts.controls;\r\n this.playerModel = opts.playerModel;\r\n this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);\r\n this.mouseSensity = opts.mouseSensity ? opts.mouseSensity : 5;\r\n\r\n const s = this.playerModel.scale;\r\n this.visualizeDepth = 0 * s;\r\n this.gravity = opts.playerModel.gravity\r\n ? opts.playerModel.gravity * s\r\n : -2400 * s;\r\n this.jumpHeight = opts.playerModel.jumpHeight\r\n ? opts.playerModel.jumpHeight * s\r\n : 800 * s;\r\n this.originPlayerSpeed = opts.playerModel.speed\r\n ? opts.playerModel.speed * s\r\n : 400 * s;\r\n this.playerSpeed = this.originPlayerSpeed;\r\n\r\n this._camCollisionLerp = 0.18;\r\n this._camEpsilon = 35 * s;\r\n this._minCamDistance = opts.minCamDistance\r\n ? opts.minCamDistance * s\r\n : 100 * s;\r\n this._maxCamDistance = opts.maxCamDistance\r\n ? opts.maxCamDistance * s\r\n : 440 * s;\r\n this.orginMaxCamDistance = this._maxCamDistance;\r\n\r\n // 创建bvh\r\n await this.createBVH(opts.colliderMeshUrl);\r\n\r\n // 创建玩家\r\n this.createPlayer();\r\n\r\n // 加载玩家模型\r\n await this.loadPersonGLB();\r\n\r\n // 等待资源加载完毕再设置摄像机\r\n if (this.isFirstPerson && this.player) {\r\n this.player.add(this.camera);\r\n }\r\n this.onAllEvent(); // 绑定事件\r\n this.setCameraPos();\r\n this.setControls();\r\n if (callback) callback();\r\n }\r\n\r\n // 第一/三视角切换\r\n changeView() {\r\n this.isFirstPerson = !this.isFirstPerson;\r\n if (this.isFirstPerson) {\r\n this.player.attach(this.camera);\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n this.camera.rotation.set(0, Math.PI, 0);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n } else {\r\n this.scene.attach(this.camera);\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n this.controls.target.copy(worldPos);\r\n document.body.requestPointerLock(); // 锁定鼠标\r\n }\r\n }\r\n\r\n // 摄像机/控制器设置\r\n setCameraPos() {\r\n if (this.isFirstPerson) {\r\n this.camera.position.set(\r\n 0,\r\n 40 * this.playerModel.scale,\r\n 30 * this.playerModel.scale\r\n );\r\n } else {\r\n const worldPos = this.player.position.clone();\r\n const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(\r\n this.player.quaternion\r\n );\r\n const angle = Math.atan2(dir.z, dir.x);\r\n const offset = new THREE.Vector3(\r\n Math.cos(angle) * 400 * this.playerModel.scale,\r\n 200 * this.playerModel.scale,\r\n Math.sin(angle) * 400 * this.playerModel.scale\r\n );\r\n this.camera.position.copy(worldPos).add(offset);\r\n }\r\n this.camera.updateProjectionMatrix();\r\n }\r\n\r\n // 设置控制器\r\n setControls() {\r\n this.controls.enabled = false;\r\n this.controls.maxPolarAngle = Math.PI * (300 / 360);\r\n }\r\n\r\n // 重置控制器\r\n resetControls() {\r\n if (!this.controls) return;\r\n this.controls.enabled = true;\r\n this.controls.enablePan = true;\r\n this.controls.maxPolarAngle = Math.PI / 2;\r\n this.controls.rotateSpeed = 1;\r\n this.controls.enableZoom = true;\r\n this.controls.mouseButtons = {\r\n LEFT: 0,\r\n MIDDLE: 1,\r\n RIGHT: 2,\r\n };\r\n }\r\n\r\n // 初始化加载器\r\n async initLoader() {\r\n const dracoLoader = new DRACOLoader();\r\n dracoLoader.setDecoderPath(\r\n \"https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/\"\r\n );\r\n dracoLoader.setDecoderConfig({ type: \"js\" });\r\n this.loader.setDRACOLoader(dracoLoader);\r\n }\r\n\r\n // 人物与动画加载\r\n async loadPersonGLB() {\r\n try {\r\n const gltf: GLTF = await this.loader.loadAsync(this.playerModel.url);\r\n this.person = gltf.scene;\r\n const sc = this.playerModel.scale;\r\n const h = this.playerHeight * sc;\r\n this.person.scale.set(sc, sc, sc);\r\n this.person.position.set(0, -h * 0.75, 0);\r\n this.person.traverse((child: any) => {\r\n if (child.isMesh) {\r\n child.castShadow = true;\r\n\r\n const mat = child.material;\r\n if (!mat) return;\r\n\r\n // 处理多材质的情况\r\n const mats = Array.isArray(mat) ? mat : [mat];\r\n\r\n mats.forEach((m) => {\r\n // console.log(\"mat\", m);\r\n // m.envMap = this.scene.background;\r\n // m.envMapIntensity = 1.0;\r\n });\r\n }\r\n });\r\n this.player.add(this.person);\r\n this.reset();\r\n\r\n // 创建人物 mixer 与 actions\r\n this.personMixer = new THREE.AnimationMixer(this.person);\r\n const animations = gltf.animations ?? [];\r\n this.personActions = new Map<string, THREE.AnimationAction>();\r\n // 取出动作并注册到 map\r\n const findClip = (name: string) =>\r\n animations.find((a: any) => a.name === name);\r\n const regs: [string, string][] = [\r\n [this.playerModel.idleAnim, \"idle\"],\r\n [this.playerModel.walkAnim, \"walking\"],\r\n [\r\n this.playerModel.leftWalkAnim || this.playerModel.walkAnim,\r\n \"left_walking\",\r\n ],\r\n [\r\n this.playerModel.rightWalkAnim || this.playerModel.walkAnim,\r\n \"right_walking\",\r\n ],\r\n [\r\n this.playerModel.backwardAnim || this.playerModel.walkAnim,\r\n \"walking_backward\",\r\n ],\r\n [this.playerModel.jumpAnim, \"jumping\"],\r\n [this.playerModel.runAnim, \"running\"],\r\n [this.playerModel.flyIdleAnim || this.playerModel.idleAnim, \"flyidle\"],\r\n [this.playerModel.flyAnim || this.playerModel.idleAnim, \"flying\"],\r\n ];\r\n\r\n // 注册动作并设置循环模式\r\n for (const [key, clipName] of regs) {\r\n const clip = findClip(key);\r\n if (!clip) continue;\r\n const action = this.personMixer.clipAction(clip);\r\n\r\n if (clipName === \"jumping\") {\r\n action.setLoop(THREE.LoopOnce, 1); // 播放一次\r\n action.clampWhenFinished = true;\r\n action.setEffectiveTimeScale(1.2); // 播放速度\r\n } else {\r\n action.setLoop(THREE.LoopRepeat, Infinity); // 循环播放\r\n action.clampWhenFinished = false;\r\n action.setEffectiveTimeScale(1);\r\n }\r\n\r\n action.enabled = true; // 激活\r\n action.setEffectiveWeight(0); // 初始权重为0\r\n this.personActions.set(clipName, action);\r\n }\r\n\r\n // 把actions激活\r\n this.idleAction = this.personActions.get(\"idle\")!;\r\n this.walkAction = this.personActions.get(\"walking\")!;\r\n this.leftWalkAction = this.personActions.get(\"left_walking\")!;\r\n this.rightWalkAction = this.personActions.get(\"right_walking\")!;\r\n this.backwardAction = this.personActions.get(\"walking_backward\")!;\r\n this.jumpAction = this.personActions.get(\"jumping\")!;\r\n this.runAction = this.personActions.get(\"running\")!;\r\n this.flyidleAction = this.personActions.get(\"flyidle\")!;\r\n this.flyAction = this.personActions.get(\"flying\")!;\r\n\r\n // 激活空闲动作\r\n this.idleAction.setEffectiveWeight(1);\r\n this.idleAction.play();\r\n this.actionState = this.idleAction;\r\n\r\n this.personMixer.addEventListener(\"finished\", (ev: any) => {\r\n const finishedAction: THREE.AnimationAction = ev.action;\r\n\r\n if (finishedAction === this.jumpAction) {\r\n // jump 播放结束后的逻辑\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) this.playPersonAnimationByName(\"running\");\r\n else this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n if (this.rgtPressed || this.lftPressed) {\r\n this.playPersonAnimationByName(\"walking\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"idle\");\r\n }\r\n });\r\n } catch (error) {}\r\n }\r\n\r\n // 平滑切换人物动画\r\n playPersonAnimationByName(name: string, fade = 0.18) {\r\n // console.log(\"播放动画\", name);\r\n if (!this.personActions) return;\r\n if (this.ctPressed) return;\r\n\r\n const next = this.personActions.get(name);\r\n if (!next) return;\r\n\r\n // 如果是同一个action,直接返回\r\n if (this.actionState === next) return;\r\n\r\n const prev = this.actionState;\r\n\r\n // 对于一次性动作先reset()\r\n next.reset();\r\n next.setEffectiveWeight(1);\r\n next.play();\r\n\r\n if (prev && prev !== next) {\r\n // 让 prev 淡出,next 淡入\r\n prev.fadeOut(fade);\r\n next.fadeIn(fade);\r\n } else {\r\n // 时直接淡入\r\n next.fadeIn(fade);\r\n }\r\n\r\n this.actionState = next;\r\n }\r\n\r\n // 创建玩家胶囊体\r\n createPlayer() {\r\n const material = new THREE.MeshStandardMaterial({\r\n color: new THREE.Color(1, 0, 0),\r\n shadowSide: THREE.DoubleSide,\r\n depthTest: false,\r\n });\r\n material.transparent = true;\r\n material.opacity = this.displayPlayer ? 0.5 : 0;\r\n material.wireframe = true;\r\n material.depthWrite = false;\r\n\r\n const r = this.playerRadius * this.playerModel.scale;\r\n const h = this.playerHeight * this.playerModel.scale;\r\n this.player = new THREE.Mesh(\r\n new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75),\r\n material\r\n ) as typeof this.player;\r\n\r\n this.player.geometry.translate(0, -h * 0.25, 0);\r\n this.player.capsuleInfo = {\r\n radius: r,\r\n segment: new THREE.Line3(\r\n new THREE.Vector3(),\r\n new THREE.Vector3(0, -h * 0.5, 0)\r\n ),\r\n };\r\n\r\n this.player.name = \"capsule\";\r\n this.scene.add(this.player);\r\n this.reset();\r\n }\r\n\r\n // 获取法线与Y轴的夹角\r\n getAngleWithYAxis(normal: { x: number; y: number; z: number }): number {\r\n // Y轴正方向向量\r\n const yAxis = { x: 0, y: 1, z: 0 };\r\n\r\n // 向量点积\r\n const dotProduct =\r\n normal.x * yAxis.x + normal.y * yAxis.y + normal.z * yAxis.z;\r\n\r\n // 向量模长\r\n const normalMagnitude = Math.sqrt(\r\n normal.x * normal.x + normal.y * normal.y + normal.z * normal.z\r\n );\r\n const yAxisMagnitude = 1; // Y轴单位向量长度为1\r\n\r\n // 计算夹角余弦值\r\n const cosTheta = dotProduct / (normalMagnitude * yAxisMagnitude);\r\n\r\n // 返回夹角(弧度)\r\n return Math.acos(cosTheta);\r\n }\r\n\r\n // 每帧更新\r\n async update(delta: number = clock.getDelta()) {\r\n if (!this.isupdate || !this.player || !this.collider) return;\r\n delta = Math.min(delta, 1 / 30);\r\n\r\n if (!this.isFlying)\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.updateMixers(delta);\r\n this.camera.getWorldDirection(this.camDir);\r\n let angle = Math.atan2(this.camDir.z, this.camDir.x) + Math.PI / 2;\r\n angle = 2 * Math.PI - angle;\r\n\r\n this.moveDir.set(0, 0, 0);\r\n if (this.fwdPressed) this.moveDir.add(this.DIR_FWD);\r\n if (this.bkdPressed) this.moveDir.add(this.DIR_BKD);\r\n if (this.lftPressed) this.moveDir.add(this.DIR_LFT);\r\n if (this.rgtPressed) this.moveDir.add(this.DIR_RGT);\r\n if (this.isFlying) {\r\n if (this.fwdPressed) {\r\n this.moveDir.y = this.camDir.y;\r\n } else {\r\n this.moveDir.y = 0;\r\n }\r\n if (this.spacePressed) {\r\n this.moveDir.add(this.DIR_UP);\r\n }\r\n }\r\n // 设置速度\r\n if (this.isFlying && this.fwdPressed) {\r\n this.playerSpeed = this.shiftPressed\r\n ? this.originPlayerSpeed * 12\r\n : this.originPlayerSpeed * 7;\r\n } else {\r\n this.playerSpeed = this.shiftPressed\r\n ? this.originPlayerSpeed * 2\r\n : this.originPlayerSpeed;\r\n }\r\n this.moveDir.normalize().applyAxisAngle(this.upVector, angle);\r\n this.player.position.addScaledVector(\r\n this.moveDir,\r\n this.playerSpeed * delta\r\n );\r\n\r\n // 向下射线检测地面高度\r\n let playerDistanceFromGround = Infinity;\r\n this._originTmp.set(\r\n this.player.position.x,\r\n this.player.position.y,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n playerDistanceFromGround = this.player.position.y - intersects[0].point.y;\r\n const normal = intersects[0].normal as THREE.Vector3;\r\n const angle = (this.getAngleWithYAxis(normal) * 180) / Math.PI;\r\n const maxH = this.playerHeight * this.playerModel.scale * 0.9; // 坡度高度阈值\r\n const h = this.playerHeight * this.playerModel.scale * 0.75; // 正常高度\r\n const minH = this.playerHeight * this.playerModel.scale * 0.7; // 最小高度\r\n\r\n if (this.isFlying) {\r\n } else {\r\n if (playerDistanceFromGround > maxH) {\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = false;\r\n } else if (\r\n playerDistanceFromGround > h &&\r\n playerDistanceFromGround < maxH\r\n ) {\r\n if (angle >= 0 && angle < 5) {\r\n // 平地\r\n // this.player.position.y = intersects[0].point.y + h;\r\n // this.playerVelocity.set(0, 0, 0);\r\n this.playerVelocity.y += delta * this.gravity;\r\n this.player.position.addScaledVector(this.playerVelocity, delta);\r\n this.playerIsOnGround = true;\r\n } else {\r\n // 坡地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n }\r\n } else if (\r\n playerDistanceFromGround > minH &&\r\n playerDistanceFromGround < h\r\n ) {\r\n // 误差范围内 在平地\r\n this.playerVelocity.set(0, 0, 0);\r\n this.playerIsOnGround = true;\r\n } else if (playerDistanceFromGround < minH) {\r\n // 强行拉回\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.set(\r\n this.player.position.x,\r\n intersects[0].point.y + h,\r\n this.player.position.z\r\n );\r\n this.playerIsOnGround = true;\r\n }\r\n }\r\n }\r\n\r\n // 更新玩家矩阵\r\n this.player.updateMatrixWorld();\r\n // 碰撞检测\r\n const capsuleInfo = this.player.capsuleInfo;\r\n this.tempBox.makeEmpty();\r\n this.tempMat.copy(this.collider!.matrixWorld).invert();\r\n this.tempSegment.copy(capsuleInfo.segment);\r\n this.tempSegment.start\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n this.tempSegment.end\r\n .applyMatrix4(this.player.matrixWorld)\r\n .applyMatrix4(this.tempMat);\r\n\r\n this.tempBox.expandByPoint(this.tempSegment.start);\r\n this.tempBox.expandByPoint(this.tempSegment.end);\r\n this.tempBox.expandByScalar(capsuleInfo.radius);\r\n\r\n const bvh = this.collider?.geometry as any;\r\n bvh?.boundsTree?.shapecast({\r\n // 检测包围盒碰撞\r\n intersectsBounds: (box: THREE.Box3) => box.intersectsBox(this.tempBox),\r\n // 检测三角形碰撞\r\n intersectsTriangle: (tri: any) => {\r\n const triPoint = this.tempVector;\r\n const capsulePoint = this.tempVector2;\r\n const distance = tri.closestPointToSegment(\r\n this.tempSegment,\r\n triPoint,\r\n capsulePoint\r\n );\r\n // 距离小于人物半径,发生碰撞\r\n if (distance < capsuleInfo.radius) {\r\n const depth = capsuleInfo.radius - distance;\r\n const direction = capsulePoint.sub(triPoint).normalize();\r\n this.tempSegment.start.addScaledVector(direction, depth);\r\n this.tempSegment.end.addScaledVector(direction, depth);\r\n }\r\n },\r\n });\r\n\r\n // 设置玩家位置\r\n const newPosition = this.tempVector\r\n .copy(this.tempSegment.start)\r\n .applyMatrix4(this.collider!.matrixWorld);\r\n const deltaVector = this.tempVector2.subVectors(\r\n newPosition,\r\n this.player.position\r\n );\r\n // 应用位移\r\n const offset = Math.max(0, deltaVector.length() - 1e-5);\r\n deltaVector.normalize().multiplyScalar(offset);\r\n this.player.position.add(deltaVector);\r\n\r\n // 第三人称-朝向\r\n if (!this.isFirstPerson && this.moveDir.lengthSq() > 0 && !this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position.clone().add(this.moveDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 飞行\r\n if (this.isFlying) {\r\n this.camDir.y = 0;\r\n this.camDir.normalize();\r\n this.camDir.negate();\r\n this.moveDir.normalize();\r\n this.moveDir.negate();\r\n const lookTarget = this.player.position\r\n .clone()\r\n .add(this.fwdPressed ? this.moveDir : this.camDir);\r\n this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);\r\n this.targetQuat.setFromRotationMatrix(this.targetMat);\r\n const alpha = Math.min(1, this.rotationSpeed * delta);\r\n this.player.quaternion.slerp(this.targetQuat, alpha);\r\n }\r\n\r\n // 第三人称-相机跟随\r\n if (!this.isFirstPerson) {\r\n const lookTarget = this.player.position.clone();\r\n lookTarget.y += 30 * this.playerModel.scale;\r\n this.camera.position.sub(this.controls.target); // 减去控制器向量\r\n this.controls.target.copy(lookTarget); // 设置控制器目标\r\n this.camera.position.add(lookTarget); // 设置相机位置\r\n this.controls.update(); // 更新控制器\r\n\r\n // 当视线被遮挡时判断\r\n this._personToCam.subVectors(this.camera.position, this.player.position); // 计算从player指向camera的向量(camera - player)\r\n const origin = this.player.position\r\n .clone()\r\n .add(new THREE.Vector3(0, 0, 0)); // 射线起点\r\n const direction = this._personToCam.clone().normalize(); // 方向\r\n const desiredDist = this._personToCam.length(); // 与期望距离\r\n this._raycasterPersonToCam.set(origin, direction);\r\n this._raycasterPersonToCam.far = desiredDist;\r\n\r\n // 做相交检测\r\n const intersects = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 相机拉近\r\n const hit = intersects[0]; // 找到第一个命中\r\n const safeDist = Math.max(\r\n hit.distance - this._camEpsilon,\r\n this._minCamDistance\r\n ); // 计算安全距离(hit.distance是从origin到碰撞点的距离)\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist)); // 目标相机位置 = origin + direction * safeDist\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp); // 平滑移动相机到targetCamPos\r\n } else {\r\n // 相机恢复\r\n // const dis = this.player.position.distanceTo(this.camera.position); // 计算当前人物到相机距离\r\n this._raycasterPersonToCam.far = this._maxCamDistance;\r\n // 检查预设相机位置是否有遮挡\r\n const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n // 恢复相机\r\n let safeDist = this._maxCamDistance;\r\n if (intersectsMaxDis.length) {\r\n const hitMax = intersectsMaxDis[0]; // 找到第一个命中\r\n safeDist = hitMax.distance - this._camEpsilon;\r\n }\r\n const targetCamPos = origin\r\n .clone()\r\n .add(direction.clone().multiplyScalar(safeDist));\r\n this.camera.position.lerp(targetCamPos, this._camCollisionLerp);\r\n }\r\n }\r\n\r\n // 掉出场景重置\r\n if (this.player.position.y < this.boundingBoxMinY - 1) {\r\n // 检测当前位置与碰撞体是否相交\r\n this._originTmp.set(\r\n this.player.position.x,\r\n 10000,\r\n this.player.position.z\r\n );\r\n this._raycaster.ray.origin.copy(this._originTmp);\r\n const intersects = this._raycaster.intersectObject(\r\n this.collider as THREE.Object3D,\r\n false\r\n );\r\n if (intersects.length > 0) {\r\n // 出现碰撞 说明玩家为bug意外掉落\r\n console.log(\"玩家为bug意外掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n intersects[0].point.y + 5,\r\n this.player.position.z\r\n )\r\n );\r\n } else {\r\n // 无碰撞 正常掉落\r\n console.log(\"玩家正常掉落\");\r\n this.reset(\r\n new THREE.Vector3(\r\n this.player.position.x,\r\n this.player.position.y + 15,\r\n this.player.position.z\r\n )\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 重置\r\n reset(position?: THREE.Vector3) {\r\n if (!this.player) return;\r\n this.playerVelocity.set(0, 0, 0);\r\n this.player.position.copy(position ? position : this.initPos);\r\n }\r\n\r\n // 销毁\r\n destroy() {\r\n this.offAllEvent();\r\n if (this.player) {\r\n this.player.remove(this.camera);\r\n this.scene.remove(this.player);\r\n }\r\n (this.player as any) = null;\r\n if (this.person) {\r\n this.scene.remove(this.person);\r\n this.person = null;\r\n }\r\n\r\n this.resetControls();\r\n\r\n // 清理 BVH 可视化\r\n if (this.visualizer) {\r\n this.scene.remove(this.visualizer);\r\n this.visualizer = null;\r\n }\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n\r\n controllerInstance = null;\r\n }\r\n\r\n // 事件绑定\r\n onAllEvent() {\r\n this.isupdate = true;\r\n document.body.requestPointerLock();\r\n window.addEventListener(\"keydown\", this._boundOnKeydown);\r\n window.addEventListener(\"keyup\", this._boundOnKeyup);\r\n window.addEventListener(\"mousemove\", this._mouseMove);\r\n window.addEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 事件解绑\r\n offAllEvent() {\r\n this.isupdate = false;\r\n document.exitPointerLock();\r\n window.removeEventListener(\"keydown\", this._boundOnKeydown);\r\n window.removeEventListener(\"keyup\", this._boundOnKeyup);\r\n window.removeEventListener(\"mousemove\", this._mouseMove);\r\n window.removeEventListener(\"click\", this._mouseClick);\r\n }\r\n\r\n // 键盘按下事件\r\n private _boundOnKeydown = async (e: KeyboardEvent) => {\r\n if (\r\n e.ctrlKey &&\r\n (e.code === \"KeyW\" ||\r\n e.code === \"KeyA\" ||\r\n e.code === \"KeyS\" ||\r\n e.code === \"KeyD\")\r\n ) {\r\n e.preventDefault();\r\n }\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = true;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = true;\r\n if (!this.playerIsOnGround || this.isFlying) return;\r\n this.playPersonAnimationByName(\"jumping\");\r\n this.playerVelocity.y = this.jumpHeight;\r\n this.playerIsOnGround = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = true;\r\n break;\r\n case \"KeyV\":\r\n this.changeView();\r\n break;\r\n case \"KeyF\":\r\n this.isFlying = !this.isFlying;\r\n this.setAnimationByPressed();\r\n break;\r\n }\r\n };\r\n\r\n // 键盘抬起事件\r\n private _boundOnKeyup = (e: KeyboardEvent) => {\r\n switch (e.code) {\r\n case \"KeyW\":\r\n this.fwdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyS\":\r\n this.bkdPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyD\":\r\n this.rgtPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"KeyA\":\r\n this.lftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"ShiftLeft\":\r\n this.shiftPressed = false;\r\n this.setAnimationByPressed();\r\n break;\r\n case \"Space\":\r\n this.spacePressed = false;\r\n break;\r\n case \"ControlLeft\":\r\n this.ctPressed = false;\r\n break;\r\n }\r\n };\r\n\r\n // 根据按键设置人物动画\r\n setAnimationByPressed = () => {\r\n this._maxCamDistance = this.orginMaxCamDistance;\r\n if (this.isFlying) {\r\n if (!this.fwdPressed) {\r\n this.playPersonAnimationByName(\"flyidle\");\r\n return;\r\n }\r\n this.playPersonAnimationByName(\"flying\");\r\n // this._maxCamDistance = 700 * this.playerModel.scale;\r\n this._maxCamDistance = this.orginMaxCamDistance * 2;\r\n return;\r\n }\r\n\r\n if (this.playerIsOnGround) {\r\n if (\r\n !this.fwdPressed &&\r\n !this.bkdPressed &&\r\n !this.lftPressed &&\r\n !this.rgtPressed\r\n ) {\r\n this.playPersonAnimationByName(\"idle\");\r\n return;\r\n }\r\n if (this.fwdPressed) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第三人称下动画统一使用 前进 动画\r\n if (\r\n !this.isFirstPerson &&\r\n (this.lftPressed || this.rgtPressed || this.bkdPressed)\r\n ) {\r\n if (this.shiftPressed) {\r\n this.playPersonAnimationByName(\"running\");\r\n } else {\r\n this.playPersonAnimationByName(\"walking\");\r\n }\r\n return;\r\n }\r\n // 第一人称下根据方向播放不同动画\r\n if (this.lftPressed) {\r\n this.playPersonAnimationByName(\"left_walking\");\r\n return;\r\n }\r\n if (this.rgtPressed) {\r\n this.playPersonAnimationByName(\"right_walking\");\r\n return;\r\n }\r\n if (this.bkdPressed) {\r\n this.playPersonAnimationByName(\"walking_backward\");\r\n return;\r\n }\r\n } else {\r\n this.playPersonAnimationByName(\"jumping\");\r\n }\r\n };\r\n\r\n // 鼠标移动事件\r\n private _mouseMove = (e: MouseEvent) => {\r\n // 记录状态\r\n if (document.pointerLockElement !== document.body) return;\r\n if (this.isFirstPerson) {\r\n const yaw = -e.movementX * 0.0001 * this.mouseSensity;\r\n const pitch = -e.movementY * 0.0001 * this.mouseSensity;\r\n this.player.rotateY(yaw);\r\n this.camera.rotation.x = THREE.MathUtils.clamp(\r\n this.camera.rotation.x + pitch,\r\n -1.3,\r\n 1.4\r\n );\r\n } else {\r\n const sensitivity = 0.0001 * this.mouseSensity;\r\n const deltaX = -e.movementX * sensitivity;\r\n const deltaY = -e.movementY * sensitivity;\r\n // 获取目标点\r\n const target = this.player.position.clone();\r\n // 计算相机到目标的距离\r\n const distance = this.camera.position.distanceTo(target);\r\n // 计算当前角度\r\n const currentPosition = this.camera.position.clone().sub(target);\r\n let theta = Math.atan2(currentPosition.x, currentPosition.z);\r\n let phi = Math.acos(currentPosition.y / distance);\r\n // 应用旋转\r\n theta += deltaX;\r\n phi += deltaY;\r\n // 限制phi角度\r\n phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));\r\n // 计算新的相机位置\r\n const newX = distance * Math.sin(phi) * Math.sin(theta);\r\n const newY = distance * Math.cos(phi);\r\n const newZ = distance * Math.sin(phi) * Math.cos(theta);\r\n\r\n this.camera.position.set(\r\n target.x + newX,\r\n target.y + newY,\r\n target.z + newZ\r\n );\r\n this.camera.lookAt(target);\r\n }\r\n };\r\n\r\n private _mouseClick = (e: MouseEvent) => {\r\n if (document.pointerLockElement !== document.body)\r\n document.body.requestPointerLock();\r\n };\r\n\r\n // 更新模型动画\r\n private updateMixers(delta: number) {\r\n if (this.personMixer) this.personMixer.update(delta);\r\n }\r\n\r\n // BVH构建\r\n async createBVH(meshUrl: string = \"\"): Promise<void> {\r\n await this.initLoader(); // 初始化加载器\r\n\r\n const ensureAttributesMinimal = (\r\n geom: THREE.BufferGeometry\r\n ): THREE.BufferGeometry | null => {\r\n if (!geom.attributes.position) {\r\n // console.warn(\"跳过无 position 的几何体\", geom);\r\n return null;\r\n }\r\n if (!geom.attributes.normal) geom.computeVertexNormals();\r\n if (!geom.attributes.uv) {\r\n const count = geom.attributes.position.count;\r\n const dummyUV = new Float32Array(count * 2);\r\n geom.setAttribute(\"uv\", new THREE.BufferAttribute(dummyUV, 2));\r\n }\r\n return geom;\r\n };\r\n\r\n const collected: THREE.BufferGeometry[] = [];\r\n if (meshUrl == \"\") {\r\n if (this.collider) {\r\n this.scene.remove(this.collider);\r\n this.collider = null;\r\n }\r\n this.scene.traverse((c) => {\r\n const mesh = c as THREE.Mesh;\r\n if (mesh?.isMesh && mesh.geometry && c.name !== \"capsule\") {\r\n try {\r\n let geom = (mesh.geometry as THREE.BufferGeometry).clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n } catch (e) {\r\n console.warn(\"处理网格时出错:\", mesh, e);\r\n }\r\n }\r\n });\r\n\r\n if (!collected.length) {\r\n return;\r\n }\r\n\r\n // 统一属性集合\r\n type AttrMeta = { itemSize: number; arrayCtor: any; examples: number };\r\n const attrMap = new Map<string, AttrMeta>();\r\n const attrConflict = new Set<string>();\r\n\r\n for (const g of collected) {\r\n for (const name of Object.keys(g.attributes)) {\r\n const attr = g.attributes[name] as THREE.BufferAttribute;\r\n const ctor = (attr.array as any).constructor;\r\n const itemSize = attr.itemSize;\r\n if (!attrMap.has(name)) {\r\n attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });\r\n } else {\r\n const m = attrMap.get(name)!;\r\n if (m.itemSize !== itemSize || m.arrayCtor !== ctor)\r\n attrConflict.add(name);\r\n else m.examples++;\r\n }\r\n }\r\n }\r\n\r\n if (attrConflict.size) {\r\n for (const g of collected) {\r\n for (const name of Array.from(attrConflict)) {\r\n if (g.attributes[name]) g.deleteAttribute(name);\r\n }\r\n }\r\n for (const name of attrConflict) attrMap.delete(name);\r\n }\r\n\r\n const attrNames = Array.from(attrMap.keys());\r\n for (const g of collected) {\r\n const count = g.attributes.position.count;\r\n for (const name of attrNames) {\r\n if (!g.attributes[name]) {\r\n const meta = attrMap.get(name)!;\r\n const len = count * meta.itemSize;\r\n const array = new meta.arrayCtor(len);\r\n g.setAttribute(\r\n name,\r\n new THREE.BufferAttribute(array, meta.itemSize)\r\n );\r\n }\r\n }\r\n }\r\n } else {\r\n const gltf: GLTF = await this.loader.loadAsync(meshUrl, (xhr) => {});\r\n const mesh = gltf.scene.children[0] as THREE.Mesh;\r\n mesh.name = \"BVH加载模型\";\r\n\r\n // 推入几何体\r\n let geom = mesh.geometry.clone();\r\n geom.applyMatrix4(mesh.matrixWorld);\r\n if (geom.index) geom = geom.toNonIndexed();\r\n const safe = ensureAttributesMinimal(geom);\r\n if (safe) collected.push(safe);\r\n }\r\n\r\n // 合并几何体\r\n const merged = BufferGeometryUtils.mergeGeometries(collected, false);\r\n if (!merged) {\r\n console.error(\"合并几何失败\");\r\n return;\r\n }\r\n\r\n // 构建bvh\r\n (merged as any).boundsTree = new MeshBVH(merged, {\r\n maxDepth: 100,\r\n });\r\n this.collider = new THREE.Mesh(\r\n merged,\r\n new THREE.MeshBasicMaterial({\r\n opacity: 0.5,\r\n transparent: true,\r\n wireframe: true,\r\n })\r\n );\r\n\r\n if (this.displayCollider) this.scene.add(this.collider);\r\n if (this.displayVisualizer) {\r\n if (this.visualizer) this.scene.remove(this.visualizer);\r\n this.visualizer = new MeshBVHHelper(this.collider, this.visualizeDepth);\r\n this.scene.add(this.visualizer);\r\n }\r\n this.boundingBoxMinY = (this.collider as any).geometry.boundingBox.min.y;\r\n // console.log(\"bvh加载模型成功\", this.collider);\r\n }\r\n}\r\n\r\n// 导出API\r\nexport function playerController() {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n const c = controllerInstance;\r\n return {\r\n init: (opts: PlayerControllerOptions, callback?: () => void) =>\r\n c.init(opts, callback),\r\n changeView: () => c.changeView(),\r\n reset: (pos?: THREE.Vector3) => c.reset(pos),\r\n update: (dt?: number) => c.update(dt),\r\n destroy: () => c.destroy(),\r\n };\r\n}\r\n\r\n// 打开所有事件\r\nexport function onAllEvent(): void {\r\n if (!controllerInstance) controllerInstance = new PlayerController();\r\n controllerInstance.onAllEvent();\r\n}\r\n\r\n// 关闭所有事件\r\nexport function offAllEvent(): void {\r\n if (!controllerInstance) return;\r\n controllerInstance.offAllEvent();\r\n}\r\n"],"mappings":"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","names":["angle","intersects"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-player-controller",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Third-person / first-person player controller for three.js (three-mesh-bvh based)",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,10 +29,14 @@
|
|
|
29
29
|
"three-mesh-bvh": "^0.9.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
+
"@types/node": "^25.0.6",
|
|
32
33
|
"@types/three": "^0.182.0",
|
|
33
34
|
"rimraf": "^6.0.0",
|
|
34
35
|
"tsup": "^8.0.1",
|
|
35
36
|
"typescript": "^5.4.0",
|
|
36
37
|
"vite": "^7.3.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"3d-tiles-renderer": "^0.4.19"
|
|
37
41
|
}
|
|
38
42
|
}
|