three-player-controller 0.3.3 → 0.3.5
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 +233 -58
- package/dist/index.d.mts +124 -13
- package/dist/index.d.ts +124 -13
- package/dist/index.js +1964 -381
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1964 -381
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -8
package/dist/index.mjs
CHANGED
|
@@ -1,31 +1,308 @@
|
|
|
1
1
|
// src/playerController.ts
|
|
2
|
-
import * as
|
|
2
|
+
import * as THREE3 from "three";
|
|
3
3
|
import { acceleratedRaycast, MeshBVH, MeshBVHHelper } from "three-mesh-bvh";
|
|
4
4
|
import { RoundedBoxGeometry } from "three/examples/jsm/geometries/RoundedBoxGeometry.js";
|
|
5
5
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
6
6
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
7
7
|
import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
|
8
8
|
|
|
9
|
+
// assets/imgs/break.png
|
|
10
|
+
var break_default = "data:image/png;base64,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";
|
|
11
|
+
|
|
9
12
|
// assets/imgs/fly.png
|
|
10
13
|
var fly_default = "data:image/png;base64,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";
|
|
11
14
|
|
|
12
15
|
// assets/imgs/jump.png
|
|
13
16
|
var jump_default = "data:image/png;base64,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";
|
|
14
17
|
|
|
18
|
+
// assets/imgs/vehicle.png
|
|
19
|
+
var vehicle_default = "data:image/png;base64,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";
|
|
20
|
+
|
|
15
21
|
// assets/imgs/view.png
|
|
16
22
|
var view_default = "data:image/png;base64,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";
|
|
17
23
|
|
|
24
|
+
// src/utils/pathPlanner.ts
|
|
25
|
+
import * as THREE from "three";
|
|
26
|
+
var PathNode = class {
|
|
27
|
+
constructor(position) {
|
|
28
|
+
this.g = Infinity;
|
|
29
|
+
// 实际代价
|
|
30
|
+
this.h = 0;
|
|
31
|
+
// 估计代价
|
|
32
|
+
this.f = Infinity;
|
|
33
|
+
// f = g + h
|
|
34
|
+
this.parent = null;
|
|
35
|
+
this.position = position.clone();
|
|
36
|
+
}
|
|
37
|
+
equals(other) {
|
|
38
|
+
return this.position.distanceTo(other.position) < 0.01;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var PriorityQueue = class {
|
|
42
|
+
constructor() {
|
|
43
|
+
this.elements = [];
|
|
44
|
+
}
|
|
45
|
+
enqueue(item, priority) {
|
|
46
|
+
this.elements.push({ priority, item });
|
|
47
|
+
this.elements.sort((a, b) => a.priority - b.priority);
|
|
48
|
+
}
|
|
49
|
+
dequeue() {
|
|
50
|
+
return this.elements.shift()?.item;
|
|
51
|
+
}
|
|
52
|
+
isEmpty() {
|
|
53
|
+
return this.elements.length === 0;
|
|
54
|
+
}
|
|
55
|
+
contains(item, compareFn) {
|
|
56
|
+
return this.elements.some((e) => compareFn(e.item, item));
|
|
57
|
+
}
|
|
58
|
+
update(item, newPriority, compareFn) {
|
|
59
|
+
const index = this.elements.findIndex((e) => compareFn(e.item, item));
|
|
60
|
+
if (index !== -1) {
|
|
61
|
+
this.elements[index].priority = newPriority;
|
|
62
|
+
this.elements.sort((a, b) => a.priority - b.priority);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var PathPlanner = class {
|
|
67
|
+
constructor(obstacleChecker, config = {}) {
|
|
68
|
+
this.debugLines = [];
|
|
69
|
+
this.debugPoints = [];
|
|
70
|
+
this.obstacleChecker = obstacleChecker;
|
|
71
|
+
this.config = {
|
|
72
|
+
debugEnabled: false,
|
|
73
|
+
scale: 1,
|
|
74
|
+
...config
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// 计算启发式距离
|
|
78
|
+
heuristic(a, b) {
|
|
79
|
+
return a.distanceTo(b);
|
|
80
|
+
}
|
|
81
|
+
// A*路径规划算法
|
|
82
|
+
findPath(start, goal) {
|
|
83
|
+
const startTime = performance.now();
|
|
84
|
+
if (!this.obstacleChecker.isBlocked(start, goal)) {
|
|
85
|
+
return [goal];
|
|
86
|
+
}
|
|
87
|
+
const navigationPoints = this.obstacleChecker.getNavigationNodes(start, goal);
|
|
88
|
+
const allNodes = [new PathNode(start), new PathNode(goal), ...navigationPoints.map((p) => new PathNode(p))];
|
|
89
|
+
if (allNodes.length < 2) {
|
|
90
|
+
console.warn("\u5BFC\u822A\u8282\u70B9\u4E0D\u8DB3\uFF0C\u8FD4\u56DE\u76F4\u7EBF\u8DEF\u5F84");
|
|
91
|
+
return [goal];
|
|
92
|
+
}
|
|
93
|
+
const startNode = allNodes[0];
|
|
94
|
+
const goalNode = allNodes[1];
|
|
95
|
+
startNode.g = 0;
|
|
96
|
+
startNode.h = this.heuristic(startNode.position, goalNode.position);
|
|
97
|
+
startNode.f = startNode.h;
|
|
98
|
+
const openList = new PriorityQueue();
|
|
99
|
+
const closedSet = /* @__PURE__ */ new Set();
|
|
100
|
+
openList.enqueue(startNode, startNode.f);
|
|
101
|
+
const nodeEquals = (a, b) => a.equals(b);
|
|
102
|
+
while (!openList.isEmpty()) {
|
|
103
|
+
const current = openList.dequeue();
|
|
104
|
+
if (!current) break;
|
|
105
|
+
if (current.equals(goalNode)) {
|
|
106
|
+
const path = this.reconstructPath(current);
|
|
107
|
+
const endTime = performance.now();
|
|
108
|
+
if (this.config.debugEnabled) {
|
|
109
|
+
this.visualizePath([start, ...path]);
|
|
110
|
+
}
|
|
111
|
+
return path;
|
|
112
|
+
}
|
|
113
|
+
closedSet.add(current);
|
|
114
|
+
for (const neighbor of allNodes) {
|
|
115
|
+
if (closedSet.has(neighbor)) continue;
|
|
116
|
+
if (this.obstacleChecker.isBlocked(current.position, neighbor.position)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const tentativeG = current.g + current.position.distanceTo(neighbor.position);
|
|
120
|
+
if (tentativeG < neighbor.g) {
|
|
121
|
+
neighbor.parent = current;
|
|
122
|
+
neighbor.g = tentativeG;
|
|
123
|
+
neighbor.h = this.heuristic(neighbor.position, goalNode.position);
|
|
124
|
+
neighbor.f = neighbor.g + neighbor.h;
|
|
125
|
+
if (openList.contains(neighbor, nodeEquals)) {
|
|
126
|
+
openList.update(neighbor, neighbor.f, nodeEquals);
|
|
127
|
+
} else {
|
|
128
|
+
openList.enqueue(neighbor, neighbor.f);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
console.warn("A*\u672A\u627E\u5230\u8DEF\u5F84\uFF0C\u4F7F\u7528\u76F4\u7EBF\u8DEF\u5F84");
|
|
134
|
+
return [goal];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 重建路径
|
|
138
|
+
*/
|
|
139
|
+
reconstructPath(endNode) {
|
|
140
|
+
const path = [];
|
|
141
|
+
let current = endNode;
|
|
142
|
+
while (current !== null) {
|
|
143
|
+
path.unshift(current.position.clone());
|
|
144
|
+
current = current.parent;
|
|
145
|
+
}
|
|
146
|
+
if (path.length > 0) {
|
|
147
|
+
path.shift();
|
|
148
|
+
}
|
|
149
|
+
return this.smoothPath(path);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 路径平滑
|
|
153
|
+
*/
|
|
154
|
+
smoothPath(path) {
|
|
155
|
+
if (path.length <= 2) return path;
|
|
156
|
+
const smoothed = [path[0]];
|
|
157
|
+
let current = 0;
|
|
158
|
+
while (current < path.length - 1) {
|
|
159
|
+
let farthest = current + 1;
|
|
160
|
+
for (let i = path.length - 1; i > current + 1; i--) {
|
|
161
|
+
if (!this.obstacleChecker.isBlocked(path[current], path[i])) {
|
|
162
|
+
farthest = i;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
smoothed.push(path[farthest]);
|
|
167
|
+
current = farthest;
|
|
168
|
+
}
|
|
169
|
+
return smoothed;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 可视化路径
|
|
173
|
+
*/
|
|
174
|
+
visualizePath(path) {
|
|
175
|
+
if (!this.config.scene || !this.config.debugEnabled) return;
|
|
176
|
+
this.clearVisualization();
|
|
177
|
+
const scale = this.config.scale || 1;
|
|
178
|
+
if (path.length > 1) {
|
|
179
|
+
const points = path.map((p) => p.clone());
|
|
180
|
+
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
181
|
+
const material = new THREE.LineBasicMaterial({
|
|
182
|
+
color: 65280,
|
|
183
|
+
linewidth: 3
|
|
184
|
+
});
|
|
185
|
+
const line = new THREE.Line(geometry, material);
|
|
186
|
+
this.config.scene.add(line);
|
|
187
|
+
this.debugLines.push(line);
|
|
188
|
+
}
|
|
189
|
+
path.forEach((point, index) => {
|
|
190
|
+
const geometry = new THREE.SphereGeometry(20 * scale);
|
|
191
|
+
const material = new THREE.MeshBasicMaterial({
|
|
192
|
+
color: index === path.length - 1 ? 16711680 : 65280
|
|
193
|
+
});
|
|
194
|
+
const sphere = new THREE.Mesh(geometry, material);
|
|
195
|
+
sphere.position.copy(point);
|
|
196
|
+
this.config.scene.add(sphere);
|
|
197
|
+
this.debugPoints.push(sphere);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 清除路径可视化
|
|
202
|
+
*/
|
|
203
|
+
clearVisualization() {
|
|
204
|
+
if (!this.config.scene) return;
|
|
205
|
+
this.debugLines.forEach((line) => {
|
|
206
|
+
this.config.scene.remove(line);
|
|
207
|
+
line.geometry.dispose();
|
|
208
|
+
line.material.dispose();
|
|
209
|
+
});
|
|
210
|
+
this.debugLines = [];
|
|
211
|
+
this.debugPoints.forEach((point) => {
|
|
212
|
+
this.config.scene.remove(point);
|
|
213
|
+
point.geometry.dispose();
|
|
214
|
+
point.material.dispose();
|
|
215
|
+
});
|
|
216
|
+
this.debugPoints = [];
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 更新配置
|
|
220
|
+
*/
|
|
221
|
+
updateConfig(config) {
|
|
222
|
+
this.config = { ...this.config, ...config };
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 销毁
|
|
226
|
+
*/
|
|
227
|
+
dispose() {
|
|
228
|
+
this.clearVisualization();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/utils/useVehicleController.ts
|
|
233
|
+
import * as THREE2 from "three";
|
|
234
|
+
function createVehicleController(world, chassisBody, wheels, wheelsInfo) {
|
|
235
|
+
if (!world || !chassisBody) return { vehicle: null, updateWheelVisuals: () => {
|
|
236
|
+
} };
|
|
237
|
+
const vehicle = world.createVehicleController(chassisBody);
|
|
238
|
+
const suspensionDirection = new THREE2.Vector3(0, -1, 0);
|
|
239
|
+
wheelsInfo.forEach((wheel, index) => {
|
|
240
|
+
vehicle.addWheel(wheel.position, suspensionDirection, wheel.axleCs, wheel.suspensionRestLength, wheel.radius);
|
|
241
|
+
vehicle.setWheelChassisConnectionPointCs(index, wheel.position);
|
|
242
|
+
vehicle.setWheelDirectionCs(index, suspensionDirection);
|
|
243
|
+
vehicle.setWheelAxleCs(index, wheel.axleCs);
|
|
244
|
+
vehicle.setWheelSuspensionRestLength(index, wheel.suspensionRestLength);
|
|
245
|
+
vehicle.setWheelRadius(index, wheel.radius);
|
|
246
|
+
vehicle.setWheelMaxSuspensionTravel(index, wheel.suspensionRestLength * 1);
|
|
247
|
+
vehicle.setWheelSuspensionStiffness(index, 250);
|
|
248
|
+
vehicle.setWheelSuspensionCompression(index, 6);
|
|
249
|
+
vehicle.setWheelSuspensionRelaxation(index, 6);
|
|
250
|
+
vehicle.setWheelMaxSuspensionForce(index, 1e4);
|
|
251
|
+
vehicle.setWheelBrake(index, 0);
|
|
252
|
+
vehicle.setWheelSteering(index, 0);
|
|
253
|
+
vehicle.setWheelEngineForce(index, 0);
|
|
254
|
+
vehicle.setWheelFrictionSlip(index, 20);
|
|
255
|
+
vehicle.setWheelSideFrictionStiffness(index, 2);
|
|
256
|
+
});
|
|
257
|
+
const up = new THREE2.Vector3(0, 1, 0);
|
|
258
|
+
const _wheelSteeringQuat = new THREE2.Quaternion();
|
|
259
|
+
const _wheelRotationQuat = new THREE2.Quaternion();
|
|
260
|
+
function updateWheelVisuals() {
|
|
261
|
+
for (const [index, wheelObj] of wheels.entries()) {
|
|
262
|
+
if (!wheelObj) continue;
|
|
263
|
+
try {
|
|
264
|
+
const wheelAxleCs = vehicle.wheelAxleCs(index) ?? new THREE2.Vector3(1, 0, 0);
|
|
265
|
+
const connection = vehicle.wheelChassisConnectionPointCs(index)?.y ?? 0;
|
|
266
|
+
const suspension = vehicle.wheelSuspensionLength(index) ?? 0;
|
|
267
|
+
const steering = vehicle.wheelSteering(index) ?? 0;
|
|
268
|
+
const rotationRad = vehicle.wheelRotation(index) ?? 0;
|
|
269
|
+
wheelObj.position.y = connection - suspension;
|
|
270
|
+
_wheelSteeringQuat.setFromAxisAngle(up, steering);
|
|
271
|
+
_wheelRotationQuat.setFromAxisAngle(wheelAxleCs, rotationRad);
|
|
272
|
+
wheelObj.quaternion.copy(_wheelSteeringQuat).multiply(_wheelRotationQuat);
|
|
273
|
+
} catch (e) {
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function destroy() {
|
|
278
|
+
try {
|
|
279
|
+
world.removeVehicleController(vehicle);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
vehicle,
|
|
285
|
+
updateWheelVisuals,
|
|
286
|
+
destroy
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
18
290
|
// src/playerController.ts
|
|
19
|
-
|
|
291
|
+
THREE3.Mesh.prototype.raycast = acceleratedRaycast;
|
|
20
292
|
var controllerInstance = null;
|
|
21
|
-
var clock = new
|
|
293
|
+
var clock = new THREE3.Clock();
|
|
22
294
|
var PlayerController = class {
|
|
23
295
|
constructor() {
|
|
24
296
|
// ==================== 基本配置与参数 ====================
|
|
25
297
|
this.loader = new GLTFLoader();
|
|
298
|
+
this.controllerMode = 0;
|
|
299
|
+
// 0: 人物 1: 车辆
|
|
300
|
+
this.enableOverShoulderView = false;
|
|
301
|
+
this.isChangeControllerTransitionTimer = null;
|
|
26
302
|
// ==================== 玩家基本属性 ====================
|
|
27
303
|
this.playerRadius = 45;
|
|
28
304
|
this.playerHeight = 180;
|
|
305
|
+
// 玩家参考身高
|
|
29
306
|
this.isFirstPerson = false;
|
|
30
307
|
this.boundingBoxMinY = 0;
|
|
31
308
|
// ==================== 测试参数 ====================
|
|
@@ -36,7 +313,66 @@ var PlayerController = class {
|
|
|
36
313
|
this.collider = null;
|
|
37
314
|
this.visualizer = null;
|
|
38
315
|
this.person = null;
|
|
39
|
-
this.
|
|
316
|
+
this.personHead = null;
|
|
317
|
+
this.collected = [];
|
|
318
|
+
this.dynamicCollider = null;
|
|
319
|
+
this.dynamicCollected = [];
|
|
320
|
+
// ==================== 多车辆相关 ====================
|
|
321
|
+
this.vehicles = [];
|
|
322
|
+
// 所有已加载车辆
|
|
323
|
+
this.activeVehicle = null;
|
|
324
|
+
// 当前驾驶/交互的车辆
|
|
325
|
+
this.vehicleLength = 6;
|
|
326
|
+
// 车辆参考长度
|
|
327
|
+
this.wheelSteeringQuat = new THREE3.Quaternion();
|
|
328
|
+
this.wheelRotationQuat = new THREE3.Quaternion();
|
|
329
|
+
this.RAPIER = null;
|
|
330
|
+
this.world = null;
|
|
331
|
+
// 全局车辆共享参数
|
|
332
|
+
this.vehicleParams = {
|
|
333
|
+
debug: {
|
|
334
|
+
showPhysicsBox: true
|
|
335
|
+
},
|
|
336
|
+
chassis: {
|
|
337
|
+
linearDamping: 0.5,
|
|
338
|
+
angularDamping: 0.5
|
|
339
|
+
},
|
|
340
|
+
model: {
|
|
341
|
+
rotation: -Math.PI / 2
|
|
342
|
+
},
|
|
343
|
+
power: {
|
|
344
|
+
accelerateForce: 50,
|
|
345
|
+
// 推进
|
|
346
|
+
brakeForce: 200,
|
|
347
|
+
// 刹车
|
|
348
|
+
maxSpeed: 1e4
|
|
349
|
+
// 最大速度
|
|
350
|
+
},
|
|
351
|
+
steering: {
|
|
352
|
+
maxSteerAngle: Math.PI / 4,
|
|
353
|
+
steerSpeed: 0.5,
|
|
354
|
+
steerReturnSpeed: 1
|
|
355
|
+
},
|
|
356
|
+
followVehicleDirection: true
|
|
357
|
+
};
|
|
358
|
+
this.camBehindDir = new THREE3.Vector3(0, 0, 1);
|
|
359
|
+
// ==================== 上车相关 ====================
|
|
360
|
+
this.isMovingToBoardingPoint = false;
|
|
361
|
+
this.boardingWaypoints = [];
|
|
362
|
+
this.currentWaypointIndex = 0;
|
|
363
|
+
this.boardingTargetDir = null;
|
|
364
|
+
this.boardingMoveSpeed = 300;
|
|
365
|
+
this.boardingRotateSpeed = 10;
|
|
366
|
+
this.flip180Quat = new THREE3.Quaternion().setFromAxisAngle(
|
|
367
|
+
new THREE3.Vector3(0, 1, 0),
|
|
368
|
+
Math.PI
|
|
369
|
+
);
|
|
370
|
+
this.closeVehicleDoorTimer = null;
|
|
371
|
+
this.boardingPointWorld = null;
|
|
372
|
+
this.isBoardingAnimPlaying = false;
|
|
373
|
+
this.closeDoorTriggered = false;
|
|
374
|
+
this.isExitAnimPlaying = false;
|
|
375
|
+
this.closeExitDoorTriggered = false;
|
|
40
376
|
// ==================== 状态开关 ====================
|
|
41
377
|
this.playerIsOnGround = false;
|
|
42
378
|
this.isupdate = true;
|
|
@@ -58,59 +394,92 @@ var PlayerController = class {
|
|
|
58
394
|
this.jumpBtnEl = null;
|
|
59
395
|
this.flyBtnEl = null;
|
|
60
396
|
this.viewBtnEl = null;
|
|
397
|
+
this.vehicleBtnEl = null;
|
|
61
398
|
this.lookPointerId = null;
|
|
62
399
|
this.isLookDown = false;
|
|
63
400
|
this.lastTouchX = 0;
|
|
64
401
|
this.lastTouchY = 0;
|
|
402
|
+
this.nearCheckLocal = new THREE3.Vector3();
|
|
403
|
+
this.nearCheckWorld = new THREE3.Vector3();
|
|
404
|
+
this.isNearVehicle = false;
|
|
65
405
|
// ==================== 第三人称相机参数 ====================
|
|
66
406
|
this._camCollisionLerp = 0.18;
|
|
67
|
-
// 平滑系数
|
|
68
407
|
this._camEpsilon = 0.35;
|
|
69
|
-
|
|
70
|
-
this.
|
|
71
|
-
// 摄像机最小距离
|
|
72
|
-
this._maxCamDistance = 4.4;
|
|
73
|
-
// 摄像机最大距离
|
|
408
|
+
this.minCamDistance = 1;
|
|
409
|
+
this.maxCamDistance = 4.4;
|
|
74
410
|
this.orginMaxCamDistance = 4.4;
|
|
75
411
|
// ==================== 物理/运动 ====================
|
|
76
|
-
this.playerVelocity = new
|
|
77
|
-
|
|
78
|
-
this.upVector = new THREE.Vector3(0, 1, 0);
|
|
412
|
+
this.playerVelocity = new THREE3.Vector3();
|
|
413
|
+
this.upVector = new THREE3.Vector3(0, 1, 0);
|
|
79
414
|
// ==================== 临时复用向量/矩阵 ====================
|
|
80
|
-
this.tempVector = new
|
|
81
|
-
this.tempVector2 = new
|
|
82
|
-
this.tempBox = new
|
|
83
|
-
this.tempMat = new
|
|
84
|
-
this.tempSegment = new
|
|
415
|
+
this.tempVector = new THREE3.Vector3();
|
|
416
|
+
this.tempVector2 = new THREE3.Vector3();
|
|
417
|
+
this.tempBox = new THREE3.Box3();
|
|
418
|
+
this.tempMat = new THREE3.Matrix4();
|
|
419
|
+
this.tempSegment = new THREE3.Line3();
|
|
85
420
|
this.recheckAnimTimer = null;
|
|
86
421
|
// ==================== 相机朝向/移动复用向量 ====================
|
|
87
|
-
this.camDir = new
|
|
88
|
-
this.moveDir = new
|
|
89
|
-
this.targetQuat = new
|
|
90
|
-
this.targetMat = new
|
|
422
|
+
this.camDir = new THREE3.Vector3();
|
|
423
|
+
this.moveDir = new THREE3.Vector3();
|
|
424
|
+
this.targetQuat = new THREE3.Quaternion();
|
|
425
|
+
this.targetMat = new THREE3.Matrix4();
|
|
91
426
|
this.rotationSpeed = 10;
|
|
92
|
-
this.DIR_FWD = new
|
|
93
|
-
this.DIR_BKD = new
|
|
94
|
-
this.DIR_LFT = new
|
|
95
|
-
this.DIR_RGT = new
|
|
96
|
-
this.DIR_UP = new
|
|
427
|
+
this.DIR_FWD = new THREE3.Vector3(0, 0, -1);
|
|
428
|
+
this.DIR_BKD = new THREE3.Vector3(0, 0, 1);
|
|
429
|
+
this.DIR_LFT = new THREE3.Vector3(-1, 0, 0);
|
|
430
|
+
this.DIR_RGT = new THREE3.Vector3(1, 0, 0);
|
|
431
|
+
this.DIR_UP = new THREE3.Vector3(0, 1, 0);
|
|
97
432
|
// ==================== 射线检测 ====================
|
|
98
|
-
this._personToCam = new
|
|
99
|
-
this._originTmp = new
|
|
100
|
-
this._raycaster = new
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
433
|
+
this._personToCam = new THREE3.Vector3();
|
|
434
|
+
this._originTmp = new THREE3.Vector3();
|
|
435
|
+
this._raycaster = new THREE3.Raycaster(
|
|
436
|
+
new THREE3.Vector3(),
|
|
437
|
+
new THREE3.Vector3(0, -1, 0)
|
|
438
|
+
);
|
|
439
|
+
this._raycasterPersonToCam = new THREE3.Raycaster(
|
|
440
|
+
new THREE3.Vector3(),
|
|
441
|
+
new THREE3.Vector3()
|
|
442
|
+
);
|
|
443
|
+
this.centerRay = new THREE3.Raycaster();
|
|
444
|
+
this.centerMouse = new THREE3.Vector2();
|
|
445
|
+
// ==================== 物理与碰撞检测 ====================
|
|
446
|
+
this.ensureAttributesMinimal = (geom) => {
|
|
447
|
+
if (!geom.attributes.position) return null;
|
|
448
|
+
if (!geom.attributes.normal) geom.computeVertexNormals();
|
|
449
|
+
if (!geom.attributes.uv) {
|
|
450
|
+
const count = geom.attributes.position.count;
|
|
451
|
+
const dummyUV = new Float32Array(count * 2);
|
|
452
|
+
geom.setAttribute("uv", new THREE3.BufferAttribute(dummyUV, 2));
|
|
453
|
+
}
|
|
454
|
+
return geom;
|
|
455
|
+
};
|
|
105
456
|
this.setAnimationByPressed = () => {
|
|
106
|
-
this.
|
|
457
|
+
this.maxCamDistance = this.orginMaxCamDistance;
|
|
458
|
+
if (this.isMovingToBoardingPoint) {
|
|
459
|
+
this.isMovingToBoardingPoint = false;
|
|
460
|
+
this.boardingWaypoints = [];
|
|
461
|
+
this.currentWaypointIndex = 0;
|
|
462
|
+
this.boardingTargetDir = null;
|
|
463
|
+
}
|
|
464
|
+
if (this.isExitAnimPlaying) {
|
|
465
|
+
this.isExitAnimPlaying = false;
|
|
466
|
+
this.closeExitDoorTriggered = false;
|
|
467
|
+
}
|
|
468
|
+
if (this.isBoardingAnimPlaying) {
|
|
469
|
+
this.isBoardingAnimPlaying = false;
|
|
470
|
+
this.closeDoorTriggered = false;
|
|
471
|
+
}
|
|
472
|
+
if (this.closeVehicleDoorTimer) {
|
|
473
|
+
clearTimeout(this.closeVehicleDoorTimer);
|
|
474
|
+
this.closeVehicleDoorTimer = null;
|
|
475
|
+
}
|
|
107
476
|
if (this.isFlying) {
|
|
108
477
|
if (!this.fwdPressed) {
|
|
109
478
|
this.playPersonAnimationByName("flyidle");
|
|
110
479
|
return;
|
|
111
480
|
}
|
|
112
481
|
this.playPersonAnimationByName("flying");
|
|
113
|
-
this.
|
|
482
|
+
this.maxCamDistance = this.orginMaxCamDistance * 2;
|
|
114
483
|
return;
|
|
115
484
|
}
|
|
116
485
|
if (this.playerIsOnGround) {
|
|
@@ -119,11 +488,15 @@ var PlayerController = class {
|
|
|
119
488
|
return;
|
|
120
489
|
}
|
|
121
490
|
if (this.fwdPressed) {
|
|
122
|
-
this.playPersonAnimationByName(
|
|
491
|
+
this.playPersonAnimationByName(
|
|
492
|
+
this.shiftPressed ? "running" : "walking"
|
|
493
|
+
);
|
|
123
494
|
return;
|
|
124
495
|
}
|
|
125
496
|
if (!this.isFirstPerson && (this.lftPressed || this.rgtPressed || this.bkdPressed)) {
|
|
126
|
-
this.playPersonAnimationByName(
|
|
497
|
+
this.playPersonAnimationByName(
|
|
498
|
+
this.shiftPressed ? "running" : "walking"
|
|
499
|
+
);
|
|
127
500
|
return;
|
|
128
501
|
}
|
|
129
502
|
if (this.lftPressed) {
|
|
@@ -148,37 +521,46 @@ var PlayerController = class {
|
|
|
148
521
|
}, 200);
|
|
149
522
|
};
|
|
150
523
|
// ==================== 事件处理 ====================
|
|
151
|
-
/**
|
|
152
|
-
* 键盘按下事件
|
|
153
|
-
*/
|
|
154
524
|
this._boundOnKeydown = async (e) => {
|
|
155
525
|
if (e.ctrlKey && ["KeyW", "KeyA", "KeyS", "KeyD"].includes(e.code)) {
|
|
156
526
|
e.preventDefault();
|
|
157
527
|
}
|
|
158
528
|
switch (e.code) {
|
|
159
529
|
case "KeyW":
|
|
530
|
+
case "ArrowUp":
|
|
160
531
|
this.fwdPressed = true;
|
|
161
532
|
this.setAnimationByPressed();
|
|
162
533
|
break;
|
|
163
534
|
case "KeyS":
|
|
535
|
+
case "ArrowDown":
|
|
164
536
|
this.bkdPressed = true;
|
|
165
537
|
this.setAnimationByPressed();
|
|
166
538
|
break;
|
|
167
539
|
case "KeyD":
|
|
540
|
+
case "ArrowRight":
|
|
168
541
|
this.rgtPressed = true;
|
|
169
542
|
this.setAnimationByPressed();
|
|
170
543
|
break;
|
|
171
544
|
case "KeyA":
|
|
545
|
+
case "ArrowLeft":
|
|
172
546
|
this.lftPressed = true;
|
|
173
547
|
this.setAnimationByPressed();
|
|
174
548
|
break;
|
|
175
549
|
case "ShiftLeft":
|
|
550
|
+
case "ShiftRight":
|
|
176
551
|
this.shiftPressed = true;
|
|
177
552
|
this.setAnimationByPressed();
|
|
178
553
|
this.controls.mouseButtons = { LEFT: 2, MIDDLE: 1, RIGHT: 0 };
|
|
179
554
|
break;
|
|
180
555
|
case "Space":
|
|
556
|
+
if (this.isMovingToBoardingPoint) {
|
|
557
|
+
this.isMovingToBoardingPoint = false;
|
|
558
|
+
this.boardingWaypoints = [];
|
|
559
|
+
this.currentWaypointIndex = 0;
|
|
560
|
+
this.boardingTargetDir = null;
|
|
561
|
+
}
|
|
181
562
|
this.spacePressed = true;
|
|
563
|
+
if (this.controllerMode == 1) return;
|
|
182
564
|
if (!this.playerIsOnGround || this.isFlying) return;
|
|
183
565
|
const next = this.personActions?.get("jumping");
|
|
184
566
|
if (next && this.actionState === next) return;
|
|
@@ -193,39 +575,48 @@ var PlayerController = class {
|
|
|
193
575
|
this.changeView();
|
|
194
576
|
break;
|
|
195
577
|
case "KeyF":
|
|
196
|
-
this.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this.
|
|
578
|
+
if (this.controllerMode == 0 && this.playerFlyEnabled) {
|
|
579
|
+
this.isFlying = !this.isFlying;
|
|
580
|
+
this.setAnimationByPressed();
|
|
581
|
+
if (!this.isFlying && !this.playerIsOnGround) {
|
|
582
|
+
this.playPersonAnimationByName("jumping");
|
|
583
|
+
}
|
|
200
584
|
}
|
|
201
585
|
break;
|
|
202
586
|
case "KeyE":
|
|
203
|
-
this.
|
|
587
|
+
if (this.isFlying) return;
|
|
588
|
+
if (this.controllerMode == 0) {
|
|
589
|
+
this.enterVehicle();
|
|
590
|
+
} else {
|
|
591
|
+
this.exitVehicle();
|
|
592
|
+
}
|
|
204
593
|
break;
|
|
205
594
|
}
|
|
206
595
|
};
|
|
207
|
-
/**
|
|
208
|
-
* 键盘抬起事件
|
|
209
|
-
*/
|
|
210
596
|
this._boundOnKeyup = (e) => {
|
|
211
597
|
switch (e.code) {
|
|
212
598
|
case "KeyW":
|
|
599
|
+
case "ArrowUp":
|
|
213
600
|
this.fwdPressed = false;
|
|
214
601
|
this.setAnimationByPressed();
|
|
215
602
|
break;
|
|
216
603
|
case "KeyS":
|
|
604
|
+
case "ArrowDown":
|
|
217
605
|
this.bkdPressed = false;
|
|
218
606
|
this.setAnimationByPressed();
|
|
219
607
|
break;
|
|
220
608
|
case "KeyD":
|
|
609
|
+
case "ArrowRight":
|
|
221
610
|
this.rgtPressed = false;
|
|
222
611
|
this.setAnimationByPressed();
|
|
223
612
|
break;
|
|
224
613
|
case "KeyA":
|
|
614
|
+
case "ArrowLeft":
|
|
225
615
|
this.lftPressed = false;
|
|
226
616
|
this.setAnimationByPressed();
|
|
227
617
|
break;
|
|
228
618
|
case "ShiftLeft":
|
|
619
|
+
case "ShiftRight":
|
|
229
620
|
this.shiftPressed = false;
|
|
230
621
|
this.setAnimationByPressed();
|
|
231
622
|
this.controls.mouseButtons = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
|
|
@@ -238,23 +629,14 @@ var PlayerController = class {
|
|
|
238
629
|
break;
|
|
239
630
|
}
|
|
240
631
|
};
|
|
241
|
-
/**
|
|
242
|
-
* 鼠标移动事件
|
|
243
|
-
*/
|
|
244
632
|
this._mouseMove = (e) => {
|
|
245
633
|
if (document.pointerLockElement !== document.body) return;
|
|
246
634
|
this.setToward(e.movementX, e.movementY, 1e-4);
|
|
247
635
|
};
|
|
248
|
-
|
|
249
|
-
* 鼠标点击事件
|
|
250
|
-
*/
|
|
251
|
-
this._mouseClick = (e) => {
|
|
636
|
+
this._mouseClick = (_e) => {
|
|
252
637
|
this.setPointerLock();
|
|
253
638
|
};
|
|
254
639
|
// ==================== 移动端控制 ====================
|
|
255
|
-
/**
|
|
256
|
-
* 指针按下事件
|
|
257
|
-
*/
|
|
258
640
|
this.onPointerDown = (e) => {
|
|
259
641
|
if (e.pointerType !== "touch") return;
|
|
260
642
|
this.isLookDown = true;
|
|
@@ -264,9 +646,6 @@ var PlayerController = class {
|
|
|
264
646
|
this.lookAreaEl?.setPointerCapture?.(e.pointerId);
|
|
265
647
|
e.preventDefault();
|
|
266
648
|
};
|
|
267
|
-
/**
|
|
268
|
-
* 指针移动事件
|
|
269
|
-
*/
|
|
270
649
|
this.onPointerMove = (e) => {
|
|
271
650
|
if (!this.isLookDown || e.pointerId !== this.lookPointerId) return;
|
|
272
651
|
const dx = e.clientX - this.lastTouchX;
|
|
@@ -276,9 +655,6 @@ var PlayerController = class {
|
|
|
276
655
|
this.setInput({ lookDeltaX: dx, lookDeltaY: dy });
|
|
277
656
|
e.preventDefault();
|
|
278
657
|
};
|
|
279
|
-
/**
|
|
280
|
-
* 指针抬起事件
|
|
281
|
-
*/
|
|
282
658
|
this.onPointerUp = (e) => {
|
|
283
659
|
if (e.pointerId !== this.lookPointerId) return;
|
|
284
660
|
this.isLookDown = false;
|
|
@@ -289,29 +665,27 @@ var PlayerController = class {
|
|
|
289
665
|
this._raycasterPersonToCam.firstHitOnly = true;
|
|
290
666
|
}
|
|
291
667
|
// ==================== 初始化相关方法 ====================
|
|
292
|
-
/**
|
|
293
|
-
* 初始化控制器
|
|
294
|
-
*/
|
|
295
668
|
async init(opts, callback) {
|
|
296
669
|
this.scene = opts.scene;
|
|
297
670
|
this.camera = opts.camera;
|
|
298
671
|
this.camera.rotation.order = "YXZ";
|
|
299
672
|
this.controls = opts.controls;
|
|
300
673
|
this.playerModel = opts.playerModel;
|
|
301
|
-
this.initPos = opts.initPos ?? new
|
|
674
|
+
this.initPos = opts.initPos ?? new THREE3.Vector3(0, 0, 0);
|
|
302
675
|
this.mouseSensity = opts.mouseSensity ?? 5;
|
|
303
676
|
const s = this.playerModel.scale;
|
|
304
|
-
this.visualizeDepth = 0 * s;
|
|
305
677
|
this.gravity = (opts.playerModel.gravity ?? -2400) * s;
|
|
306
|
-
this.jumpHeight = (opts.playerModel.jumpHeight ??
|
|
307
|
-
this.
|
|
308
|
-
this.
|
|
678
|
+
this.jumpHeight = (opts.playerModel.jumpHeight ?? 600) * s;
|
|
679
|
+
this.playerSpeed = (opts.playerModel.speed ?? 300) * s;
|
|
680
|
+
this.playerFlySpeed = (opts.playerModel.playerFlySpeed ?? 2100) * s;
|
|
681
|
+
this.curPlayerSpeed = this.playerSpeed;
|
|
309
682
|
this.playerModel.rotateY = opts.playerModel.rotateY ?? 0;
|
|
683
|
+
this.playerFlyEnabled = opts.playerModel.flyEnabled ?? true;
|
|
310
684
|
this._camCollisionLerp = 0.18;
|
|
311
685
|
this._camEpsilon = 35 * s;
|
|
312
|
-
this.
|
|
313
|
-
this.
|
|
314
|
-
this.orginMaxCamDistance = this.
|
|
686
|
+
this.minCamDistance = (opts.minCamDistance ?? 100) * s;
|
|
687
|
+
this.maxCamDistance = (opts.maxCamDistance ?? 440) * s;
|
|
688
|
+
this.orginMaxCamDistance = this.maxCamDistance;
|
|
315
689
|
this.thirdMouseMode = opts.thirdMouseMode ?? 1;
|
|
316
690
|
this.enableZoom = opts.enableZoom ?? false;
|
|
317
691
|
const isMobileDevice = () => navigator.maxTouchPoints && navigator.maxTouchPoints > 0 || "ontouchstart" in window || /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
@@ -320,47 +694,125 @@ var PlayerController = class {
|
|
|
320
694
|
await this.initMobileControls();
|
|
321
695
|
}
|
|
322
696
|
await this.createBVH(opts.colliderMeshUrl);
|
|
323
|
-
this.createPlayer();
|
|
324
697
|
await this.loadPersonGLB();
|
|
325
|
-
if (this.isFirstPerson && this.person) {
|
|
326
|
-
this.person.add(this.camera);
|
|
327
|
-
}
|
|
328
698
|
this.onAllEvent();
|
|
329
699
|
this.setCameraPos();
|
|
330
700
|
this.setControls();
|
|
331
701
|
if (callback) callback();
|
|
702
|
+
this.enableOverShoulderView = opts.enableOverShoulderView ?? false;
|
|
703
|
+
this.setOverShoulderView(this.enableOverShoulderView);
|
|
704
|
+
}
|
|
705
|
+
setOverShoulderView(enable) {
|
|
706
|
+
if (!enable || this.controllerMode == 1) {
|
|
707
|
+
this.camera.clearViewOffset();
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const w = window.innerWidth;
|
|
711
|
+
const h = window.innerHeight;
|
|
712
|
+
this.camera.setViewOffset(
|
|
713
|
+
w,
|
|
714
|
+
h,
|
|
715
|
+
-w * -0.15,
|
|
716
|
+
0,
|
|
717
|
+
w,
|
|
718
|
+
h
|
|
719
|
+
);
|
|
332
720
|
}
|
|
333
|
-
/**
|
|
334
|
-
* 初始化加载器
|
|
335
|
-
*/
|
|
336
721
|
async initLoader() {
|
|
337
722
|
const dracoLoader = new DRACOLoader();
|
|
338
723
|
dracoLoader.setDecoderPath("https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/");
|
|
339
724
|
dracoLoader.setDecoderConfig({ type: "js" });
|
|
340
725
|
this.loader.setDRACOLoader(dracoLoader);
|
|
341
726
|
}
|
|
727
|
+
async initRapier() {
|
|
728
|
+
if (this.RAPIER) return;
|
|
729
|
+
this.RAPIER = await import("@dimforge/rapier3d-compat");
|
|
730
|
+
await this.RAPIER.init();
|
|
731
|
+
const gravity = new this.RAPIER.Vector3(0, -9.81, 0);
|
|
732
|
+
this.world = new this.RAPIER.World(gravity);
|
|
733
|
+
this.world.maxCcdSubsteps = 2;
|
|
734
|
+
const addGeometryAsTrimesh = (RAPIER, world, geom) => {
|
|
735
|
+
let geometry = geom.index ? geom.clone().toNonIndexed() : geom.clone();
|
|
736
|
+
const posAttr = geometry.attributes.position;
|
|
737
|
+
const vertexCount = posAttr.count;
|
|
738
|
+
if (vertexCount % 3 !== 0) {
|
|
739
|
+
console.warn("\u9876\u70B9\u6570\u4E0D\u662F3\u7684\u500D\u6570\uFF0C\u4E09\u89D2\u5F62\u53EF\u80FD\u4E0D\u5B8C\u6574");
|
|
740
|
+
}
|
|
741
|
+
const vertices = new Float32Array(vertexCount * 3);
|
|
742
|
+
const tmp = new THREE3.Vector3();
|
|
743
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
744
|
+
tmp.fromBufferAttribute(posAttr, i);
|
|
745
|
+
vertices[i * 3 + 0] = tmp.x;
|
|
746
|
+
vertices[i * 3 + 1] = tmp.y;
|
|
747
|
+
vertices[i * 3 + 2] = tmp.z;
|
|
748
|
+
}
|
|
749
|
+
const indices = vertexCount > 65535 ? new Uint32Array(vertexCount) : new Uint16Array(vertexCount);
|
|
750
|
+
for (let i = 0; i < vertexCount; i++) indices[i] = i;
|
|
751
|
+
const bodyDesc = RAPIER.RigidBodyDesc.fixed();
|
|
752
|
+
const body = world.createRigidBody(bodyDesc);
|
|
753
|
+
const colliderDesc = RAPIER.ColliderDesc.trimesh(vertices, indices).setRestitution(0).setFriction(0.8);
|
|
754
|
+
world.createCollider(colliderDesc, body);
|
|
755
|
+
};
|
|
756
|
+
for (const g of this.collected) {
|
|
757
|
+
addGeometryAsTrimesh(this.RAPIER, this.world, g);
|
|
758
|
+
}
|
|
759
|
+
const groundDesc = this.RAPIER.RigidBodyDesc.fixed();
|
|
760
|
+
const groundBody = this.world.createRigidBody(groundDesc);
|
|
761
|
+
groundBody.userData = { outOfBounds: true };
|
|
762
|
+
}
|
|
342
763
|
// ==================== 玩家模型相关方法 ====================
|
|
343
|
-
/**
|
|
344
|
-
* 加载玩家模型与动画
|
|
345
|
-
*/
|
|
346
764
|
async loadPersonGLB() {
|
|
347
765
|
try {
|
|
348
|
-
const gltf = await this.loader.loadAsync(
|
|
766
|
+
const gltf = await this.loader.loadAsync(
|
|
767
|
+
this.playerModel.url
|
|
768
|
+
);
|
|
349
769
|
this.person = gltf.scene;
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
|
|
770
|
+
const { size } = this.getBbox(this.person);
|
|
771
|
+
const ratio = this.playerHeight / size.y;
|
|
772
|
+
const power = Math.round(Math.log10(ratio));
|
|
773
|
+
const modelScale = Math.pow(10, power);
|
|
774
|
+
this.playerRadius = Number(Math.min(size.x, size.z).toFixed(0)) * modelScale;
|
|
775
|
+
this.playerHeight = Number(size.y.toFixed(0)) * modelScale;
|
|
776
|
+
const scale = this.playerModel.scale;
|
|
777
|
+
const material = new THREE3.MeshStandardMaterial({
|
|
778
|
+
color: new THREE3.Color(1, 0, 0),
|
|
779
|
+
shadowSide: THREE3.DoubleSide,
|
|
780
|
+
depthTest: false,
|
|
781
|
+
transparent: true,
|
|
782
|
+
opacity: this.displayPlayer ? 0.5 : 0,
|
|
783
|
+
wireframe: true,
|
|
784
|
+
depthWrite: false
|
|
785
|
+
});
|
|
786
|
+
const r = this.playerRadius * scale;
|
|
787
|
+
const h = this.playerHeight * scale;
|
|
788
|
+
this.player = new THREE3.Mesh(
|
|
789
|
+
new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75),
|
|
790
|
+
material
|
|
791
|
+
);
|
|
792
|
+
this.player.geometry.translate(0, -h * 0.25, 0);
|
|
793
|
+
this.player.capsuleInfo = {
|
|
794
|
+
radius: r,
|
|
795
|
+
segment: new THREE3.Line3(
|
|
796
|
+
new THREE3.Vector3(),
|
|
797
|
+
new THREE3.Vector3(0, -h * 0.5, 0)
|
|
798
|
+
)
|
|
799
|
+
};
|
|
800
|
+
this.player.name = "capsule";
|
|
801
|
+
this.scene.add(this.player);
|
|
802
|
+
this.reset();
|
|
803
|
+
this.player.rotateY(this.playerModel.rotateY ?? 0);
|
|
804
|
+
this.person.scale.multiplyScalar(modelScale * scale);
|
|
353
805
|
this.person.position.set(0, -h * 0.75, 0);
|
|
354
806
|
this.person.traverse((child) => {
|
|
355
|
-
if (child.
|
|
356
|
-
|
|
357
|
-
child.receiveShadow = true;
|
|
807
|
+
if (child.name == this.playerModel?.headObjName) {
|
|
808
|
+
this.personHead = child;
|
|
358
809
|
}
|
|
359
810
|
});
|
|
360
811
|
this.player.add(this.person);
|
|
361
812
|
this.reset();
|
|
362
|
-
this.personMixer = new
|
|
813
|
+
this.personMixer = new THREE3.AnimationMixer(this.person);
|
|
363
814
|
const animations = gltf.animations ?? [];
|
|
815
|
+
console.log("animations", animations);
|
|
364
816
|
this.personActions = /* @__PURE__ */ new Map();
|
|
365
817
|
const animationMappings = [
|
|
366
818
|
[this.playerModel.idleAnim, "idle"],
|
|
@@ -371,7 +823,9 @@ var PlayerController = class {
|
|
|
371
823
|
[this.playerModel.jumpAnim, "jumping"],
|
|
372
824
|
[this.playerModel.runAnim, "running"],
|
|
373
825
|
[this.playerModel.flyIdleAnim || this.playerModel.idleAnim, "flyidle"],
|
|
374
|
-
[this.playerModel.flyAnim || this.playerModel.idleAnim, "flying"]
|
|
826
|
+
[this.playerModel.flyAnim || this.playerModel.idleAnim, "flying"],
|
|
827
|
+
[this.playerModel.enterCarAnim || this.playerModel.idleAnim, "enterCar"],
|
|
828
|
+
[this.playerModel.exitCarAnim || this.playerModel.idleAnim, "exitCar"]
|
|
375
829
|
];
|
|
376
830
|
const findClip = (name) => animations.find((a) => a.name === name);
|
|
377
831
|
for (const [clipName, actionName] of animationMappings) {
|
|
@@ -379,11 +833,11 @@ var PlayerController = class {
|
|
|
379
833
|
if (!clip) continue;
|
|
380
834
|
const action = this.personMixer.clipAction(clip);
|
|
381
835
|
if (actionName === "jumping") {
|
|
382
|
-
action.setLoop(
|
|
836
|
+
action.setLoop(THREE3.LoopOnce, 1);
|
|
383
837
|
action.clampWhenFinished = true;
|
|
384
838
|
action.setEffectiveTimeScale(1.2);
|
|
385
839
|
} else {
|
|
386
|
-
action.setLoop(
|
|
840
|
+
action.setLoop(THREE3.LoopRepeat, Infinity);
|
|
387
841
|
action.clampWhenFinished = false;
|
|
388
842
|
action.setEffectiveTimeScale(1);
|
|
389
843
|
}
|
|
@@ -407,7 +861,9 @@ var PlayerController = class {
|
|
|
407
861
|
const finishedAction = ev.action;
|
|
408
862
|
if (finishedAction === this.jumpAction) {
|
|
409
863
|
if (this.fwdPressed) {
|
|
410
|
-
this.playPersonAnimationByName(
|
|
864
|
+
this.playPersonAnimationByName(
|
|
865
|
+
this.shiftPressed ? "running" : "walking"
|
|
866
|
+
);
|
|
411
867
|
return;
|
|
412
868
|
}
|
|
413
869
|
if (this.bkdPressed) {
|
|
@@ -420,21 +876,70 @@ var PlayerController = class {
|
|
|
420
876
|
}
|
|
421
877
|
this.playPersonAnimationByName("idle");
|
|
422
878
|
}
|
|
879
|
+
if (finishedAction === this.personActions?.get("enterCar")) {
|
|
880
|
+
this.onEnterCarAnimFinished();
|
|
881
|
+
}
|
|
882
|
+
if (finishedAction === this.personActions?.get("exitCar")) {
|
|
883
|
+
}
|
|
423
884
|
});
|
|
424
885
|
} catch (error) {
|
|
425
886
|
console.error("\u52A0\u8F7D\u73A9\u5BB6\u6A21\u578B\u5931\u8D25:", error);
|
|
426
887
|
}
|
|
427
888
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
889
|
+
async switchPlayerModel(newPlayerModel) {
|
|
890
|
+
const savedPos = this.player.position.clone();
|
|
891
|
+
const savedQuat = this.player.quaternion.clone();
|
|
892
|
+
const wasFirstPerson = this.isFirstPerson;
|
|
893
|
+
if (wasFirstPerson) {
|
|
894
|
+
this.scene.attach(this.camera);
|
|
895
|
+
}
|
|
896
|
+
if (this.player) {
|
|
897
|
+
this.scene.remove(this.player);
|
|
898
|
+
}
|
|
899
|
+
if (this.person) {
|
|
900
|
+
this.player.remove(this.person);
|
|
901
|
+
this.person = null;
|
|
902
|
+
this.personHead = null;
|
|
903
|
+
}
|
|
904
|
+
if (this.personMixer) {
|
|
905
|
+
this.personMixer.stopAllAction();
|
|
906
|
+
this.personMixer.uncacheRoot(this.personMixer.getRoot());
|
|
907
|
+
this.personMixer = void 0;
|
|
908
|
+
this.personActions = void 0;
|
|
909
|
+
}
|
|
910
|
+
const ratio = newPlayerModel.scale / this.playerModel.scale;
|
|
911
|
+
this.playerModel = { ...this.playerModel, ...newPlayerModel };
|
|
912
|
+
this.gravity *= ratio;
|
|
913
|
+
this.jumpHeight *= ratio;
|
|
914
|
+
this.playerSpeed *= ratio;
|
|
915
|
+
this.playerFlySpeed *= ratio;
|
|
916
|
+
this.curPlayerSpeed *= ratio;
|
|
917
|
+
this._camEpsilon *= ratio;
|
|
918
|
+
this.minCamDistance *= ratio;
|
|
919
|
+
this.maxCamDistance *= ratio;
|
|
920
|
+
this.orginMaxCamDistance *= ratio;
|
|
921
|
+
await this.loadPersonGLB();
|
|
922
|
+
this.player.position.copy(savedPos);
|
|
923
|
+
this.player.quaternion.copy(savedQuat);
|
|
924
|
+
if (wasFirstPerson) {
|
|
925
|
+
this.setFirstPersonCamera();
|
|
926
|
+
}
|
|
927
|
+
this.setDebug(this.displayCollider);
|
|
928
|
+
}
|
|
431
929
|
playPersonAnimationByName(name, fade = 0.18) {
|
|
432
930
|
if (!this.personActions || this.ctPressed) return;
|
|
433
931
|
const next = this.personActions.get(name);
|
|
434
932
|
if (!next || this.actionState === next) return;
|
|
933
|
+
const duration = next.getClip().duration;
|
|
435
934
|
const prev = this.actionState;
|
|
436
935
|
next.reset();
|
|
437
936
|
next.setEffectiveWeight(1);
|
|
937
|
+
if (name == "enterCar" || name == "exitCar") {
|
|
938
|
+
const enterTime = this.activeVehicle?.enterVehicleTime ?? 1.5;
|
|
939
|
+
next.setEffectiveTimeScale(duration / enterTime);
|
|
940
|
+
next.setLoop(THREE3.LoopOnce, 1);
|
|
941
|
+
next.clampWhenFinished = true;
|
|
942
|
+
}
|
|
438
943
|
next.play();
|
|
439
944
|
if (prev && prev !== next) {
|
|
440
945
|
prev.fadeOut(fade);
|
|
@@ -444,106 +949,521 @@ var PlayerController = class {
|
|
|
444
949
|
}
|
|
445
950
|
this.actionState = next;
|
|
446
951
|
}
|
|
447
|
-
/**
|
|
448
|
-
* 创建玩家胶囊体
|
|
449
|
-
*/
|
|
450
|
-
createPlayer() {
|
|
451
|
-
const material = new THREE.MeshStandardMaterial({
|
|
452
|
-
color: new THREE.Color(1, 0, 0),
|
|
453
|
-
shadowSide: THREE.DoubleSide,
|
|
454
|
-
depthTest: false,
|
|
455
|
-
transparent: true,
|
|
456
|
-
opacity: this.displayPlayer ? 0.5 : 0,
|
|
457
|
-
wireframe: true,
|
|
458
|
-
depthWrite: false
|
|
459
|
-
});
|
|
460
|
-
const r = this.playerRadius * this.playerModel.scale;
|
|
461
|
-
const h = this.playerHeight * this.playerModel.scale;
|
|
462
|
-
this.player = new THREE.Mesh(new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75), material);
|
|
463
|
-
this.player.geometry.translate(0, -h * 0.25, 0);
|
|
464
|
-
this.player.capsuleInfo = {
|
|
465
|
-
radius: r,
|
|
466
|
-
segment: new THREE.Line3(new THREE.Vector3(), new THREE.Vector3(0, -h * 0.5, 0))
|
|
467
|
-
};
|
|
468
|
-
this.player.name = "capsule";
|
|
469
|
-
this.scene.add(this.player);
|
|
470
|
-
this.reset();
|
|
471
|
-
this.player.rotateY(this.playerModel.rotateY ?? 0);
|
|
472
|
-
}
|
|
473
952
|
// ==================== 车辆模型相关 ====================
|
|
474
953
|
/**
|
|
475
|
-
*
|
|
954
|
+
* 加载车辆模型
|
|
476
955
|
*/
|
|
477
|
-
async loadVehicleModel(
|
|
956
|
+
async loadVehicleModel(opts) {
|
|
478
957
|
try {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.
|
|
483
|
-
this.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
this.
|
|
491
|
-
|
|
492
|
-
this.
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
];
|
|
958
|
+
if (!this.playerModel.enterCarAnim) {
|
|
959
|
+
return console.warn("\u672A\u914D\u7F6E\u4E0A\u8F66\u52A8\u753B\uFF0C\u4E0D\u6267\u884C\u8F66\u8F86\u76F8\u5173\u903B\u8F91");
|
|
960
|
+
}
|
|
961
|
+
await this.initRapier();
|
|
962
|
+
if (!this.world) return;
|
|
963
|
+
const scale = opts.scale ?? 1;
|
|
964
|
+
const chassisRatio = opts.chassisRatio ?? 0.2;
|
|
965
|
+
const suspensionRestLengthRatio = opts.suspensionRestLengthRatio ?? 0.2;
|
|
966
|
+
const speedMultiplier = opts.speedMultiplier ?? 1;
|
|
967
|
+
const followVehicleDirection = opts.followVehicleDirection ?? true;
|
|
968
|
+
this.vehicleParams.power.accelerateForce = 50 * scale;
|
|
969
|
+
this.vehicleParams.power.brakeForce = 200 * scale;
|
|
970
|
+
this.vehicleParams.power.maxSpeed = 1e4 * scale;
|
|
971
|
+
this.vehicleParams.followVehicleDirection = followVehicleDirection;
|
|
972
|
+
const vehicleModel = await this.loader.loadAsync(opts.url);
|
|
973
|
+
const { size: originalSize } = this.getBbox(vehicleModel.scene);
|
|
974
|
+
const ratio = this.vehicleLength / Math.max(originalSize.x, originalSize.y, originalSize.z);
|
|
975
|
+
const power = Math.round(Math.log10(ratio));
|
|
976
|
+
const modelScale = Math.pow(10, power);
|
|
977
|
+
const vehicleMixer = new THREE3.AnimationMixer(vehicleModel.scene);
|
|
978
|
+
const animations = vehicleModel.animations ?? [];
|
|
979
|
+
const vehicleActions = /* @__PURE__ */ new Map();
|
|
500
980
|
const findClip = (name) => animations.find((a) => a.name === name);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
action.setLoop(THREE.LoopOnce, 1);
|
|
981
|
+
const openDoorClip = findClip(opts.animations?.openDoorAnim || "");
|
|
982
|
+
if (openDoorClip) {
|
|
983
|
+
const action = vehicleMixer.clipAction(openDoorClip);
|
|
984
|
+
action.setLoop(THREE3.LoopOnce, 1);
|
|
506
985
|
action.clampWhenFinished = true;
|
|
507
|
-
action.setEffectiveTimeScale(
|
|
986
|
+
action.setEffectiveTimeScale(openDoorClip.duration);
|
|
508
987
|
action.enabled = true;
|
|
509
988
|
action.setEffectiveWeight(0);
|
|
510
|
-
|
|
989
|
+
vehicleActions.set("openDoor", action);
|
|
511
990
|
}
|
|
512
|
-
|
|
991
|
+
const wheelObjects = [];
|
|
992
|
+
for (const wheelName of opts.wheelsNames) {
|
|
993
|
+
let found = false;
|
|
994
|
+
vehicleModel.scene.traverse((child) => {
|
|
995
|
+
if (child.name === wheelName && !found) {
|
|
996
|
+
wheelObjects.push(child);
|
|
997
|
+
found = true;
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
if (!found) console.warn(`\u672A\u627E\u5230\u8F6E\u5B50: ${wheelName}`);
|
|
1001
|
+
}
|
|
1002
|
+
const tempGroup = new THREE3.Group();
|
|
1003
|
+
this.scene.add(tempGroup);
|
|
1004
|
+
vehicleModel.scene.scale.multiplyScalar(modelScale * scale);
|
|
1005
|
+
vehicleModel.scene.rotateY(this.vehicleParams.model.rotation);
|
|
1006
|
+
const { size, bbox, center } = this.getBbox(vehicleModel.scene);
|
|
1007
|
+
vehicleModel.scene.position.set(-center.x, -center.y, -center.z);
|
|
1008
|
+
tempGroup.add(vehicleModel.scene);
|
|
1009
|
+
tempGroup.updateMatrixWorld(true);
|
|
1010
|
+
const wheelsInfo = [];
|
|
1011
|
+
let wheelRadius = 0;
|
|
1012
|
+
let wheelWidth = 0;
|
|
1013
|
+
let suspensionRestLength = 0;
|
|
1014
|
+
let chassisHeight = 0;
|
|
1015
|
+
let wheelSizeInit = false;
|
|
1016
|
+
for (let i = 0; i < wheelObjects.length; i++) {
|
|
1017
|
+
const wheel = wheelObjects[i];
|
|
1018
|
+
const worldPos = new THREE3.Vector3();
|
|
1019
|
+
const worldQuat = new THREE3.Quaternion();
|
|
1020
|
+
const worldScale = new THREE3.Vector3();
|
|
1021
|
+
wheel.getWorldPosition(worldPos);
|
|
1022
|
+
wheel.getWorldQuaternion(worldQuat);
|
|
1023
|
+
wheel.getWorldScale(worldScale);
|
|
1024
|
+
if (!wheelSizeInit) {
|
|
1025
|
+
const { size: ws } = this.getBbox(wheel);
|
|
1026
|
+
wheelRadius = Number((Math.max(ws.x, ws.y, ws.z) / 2).toFixed(2));
|
|
1027
|
+
wheelWidth = Number(Math.min(ws.x, ws.y, ws.z).toFixed(2));
|
|
1028
|
+
suspensionRestLength = Number((wheelRadius * 2 * suspensionRestLengthRatio).toFixed(2));
|
|
1029
|
+
chassisHeight = Number((wheelRadius * 2 * chassisRatio).toFixed(2));
|
|
1030
|
+
wheelSizeInit = true;
|
|
1031
|
+
}
|
|
1032
|
+
wheelsInfo.push({
|
|
1033
|
+
axleCs: new THREE3.Vector3(0, 0, -1),
|
|
1034
|
+
position: worldPos,
|
|
1035
|
+
quaternion: worldQuat,
|
|
1036
|
+
scale: worldScale,
|
|
1037
|
+
radius: wheelRadius,
|
|
1038
|
+
width: wheelWidth,
|
|
1039
|
+
suspensionRestLength,
|
|
1040
|
+
object: wheel
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
tempGroup.remove(vehicleModel.scene);
|
|
1044
|
+
this.scene.remove(tempGroup);
|
|
1045
|
+
const vehicleGroup = new THREE3.Group();
|
|
1046
|
+
this.scene.add(vehicleGroup);
|
|
1047
|
+
vehicleGroup.add(vehicleModel.scene);
|
|
1048
|
+
vehicleGroup.updateMatrixWorld(true);
|
|
1049
|
+
const wheelWrappers = [];
|
|
1050
|
+
for (let i = 0; i < wheelsInfo.length; i++) {
|
|
1051
|
+
const wheel = wheelsInfo[i];
|
|
1052
|
+
const localPos = vehicleGroup.worldToLocal(wheel.position.clone());
|
|
1053
|
+
const wheelWrapper = new THREE3.Group();
|
|
1054
|
+
wheelWrapper.position.copy(localPos);
|
|
1055
|
+
const wheelObj = wheelsInfo[i].object;
|
|
1056
|
+
if (wheelObj.parent) wheelObj.parent.remove(wheelObj);
|
|
1057
|
+
wheelObj.position.set(0, 0, 0);
|
|
1058
|
+
wheelObj.quaternion.copy(wheel.quaternion);
|
|
1059
|
+
wheelObj.scale.copy(wheel.scale);
|
|
1060
|
+
wheelObj.updateMatrixWorld();
|
|
1061
|
+
wheelWrapper.add(wheelObj);
|
|
1062
|
+
vehicleGroup.add(wheelWrapper);
|
|
1063
|
+
wheelWrappers.push(wheelWrapper);
|
|
1064
|
+
}
|
|
1065
|
+
const halfExtents = size.clone().multiplyScalar(0.5);
|
|
1066
|
+
halfExtents.y -= chassisHeight / 2;
|
|
1067
|
+
vehicleModel.scene.position.y -= chassisHeight / 2;
|
|
1068
|
+
halfExtents.x *= 0.95;
|
|
1069
|
+
halfExtents.z *= 0.95;
|
|
1070
|
+
const chassisDesc = this.RAPIER.RigidBodyDesc.dynamic().setTranslation(
|
|
1071
|
+
opts.position.x,
|
|
1072
|
+
opts.position.y,
|
|
1073
|
+
opts.position.z
|
|
1074
|
+
).setLinearDamping(this.vehicleParams.chassis.linearDamping).setAngularDamping(this.vehicleParams.chassis.angularDamping).setCanSleep(true).setAdditionalMass(10);
|
|
1075
|
+
const chassisBody = this.world.createRigidBody(chassisDesc);
|
|
1076
|
+
const chassisCollider = this.RAPIER.ColliderDesc.cuboid(
|
|
1077
|
+
halfExtents.x,
|
|
1078
|
+
halfExtents.y,
|
|
1079
|
+
halfExtents.z
|
|
1080
|
+
);
|
|
1081
|
+
this.world.createCollider(chassisCollider, chassisBody);
|
|
1082
|
+
if (this.vehicleParams.debug.showPhysicsBox) {
|
|
1083
|
+
const debugBox = new THREE3.Mesh(
|
|
1084
|
+
new THREE3.BoxGeometry(
|
|
1085
|
+
halfExtents.x * 2,
|
|
1086
|
+
halfExtents.y * 2,
|
|
1087
|
+
halfExtents.z * 2
|
|
1088
|
+
),
|
|
1089
|
+
new THREE3.MeshBasicMaterial({
|
|
1090
|
+
color: 16711680,
|
|
1091
|
+
wireframe: true,
|
|
1092
|
+
transparent: true,
|
|
1093
|
+
opacity: 0.3
|
|
1094
|
+
})
|
|
1095
|
+
);
|
|
1096
|
+
vehicleGroup.add(debugBox);
|
|
1097
|
+
}
|
|
1098
|
+
vehicleGroup.position.copy(opts.position);
|
|
1099
|
+
vehicleGroup.updateMatrixWorld(true);
|
|
1100
|
+
const { vehicle, updateWheelVisuals } = createVehicleController(
|
|
1101
|
+
this.world,
|
|
1102
|
+
chassisBody,
|
|
1103
|
+
wheelWrappers,
|
|
1104
|
+
wheelsInfo
|
|
1105
|
+
);
|
|
1106
|
+
const vehicleInstance = {
|
|
1107
|
+
vehicleGroup,
|
|
1108
|
+
chassisBody,
|
|
1109
|
+
vehicleController: vehicle,
|
|
1110
|
+
updateWheelVisuals,
|
|
1111
|
+
vehicleMixer,
|
|
1112
|
+
vehicleActions,
|
|
1113
|
+
vehiclIsOpenDoor: false,
|
|
1114
|
+
vehicleBBox: bbox.clone(),
|
|
1115
|
+
pathPlanner: new PathPlanner(
|
|
1116
|
+
this._createObstacleCheckerFor(vehicleGroup, bbox, scale),
|
|
1117
|
+
{
|
|
1118
|
+
debugEnabled: false,
|
|
1119
|
+
scene: this.scene,
|
|
1120
|
+
scale: this.playerModel.scale
|
|
1121
|
+
}
|
|
1122
|
+
),
|
|
1123
|
+
scale,
|
|
1124
|
+
boardingPoint: opts.boardingPoint,
|
|
1125
|
+
seatOffset: opts.seatOffset ?? new THREE3.Vector3(0, 0, 0),
|
|
1126
|
+
enterVehicleTime: 1.5,
|
|
1127
|
+
chassisRatio,
|
|
1128
|
+
suspensionRestLengthRatio,
|
|
1129
|
+
size: {
|
|
1130
|
+
l: Math.max(size.x, size.z),
|
|
1131
|
+
w: Math.min(size.x, size.z),
|
|
1132
|
+
h: size.y
|
|
1133
|
+
},
|
|
1134
|
+
speedMultiplier
|
|
1135
|
+
};
|
|
1136
|
+
this.vehicles.push(vehicleInstance);
|
|
1137
|
+
this.setControllerTransition();
|
|
513
1138
|
} catch (error) {
|
|
514
1139
|
console.error("\u52A0\u8F7D\u8F66\u8F86\u6A21\u578B\u5931\u8D25:", error);
|
|
515
1140
|
}
|
|
516
1141
|
}
|
|
517
|
-
|
|
1142
|
+
getBbox(object) {
|
|
1143
|
+
const bbox = new THREE3.Box3().setFromObject(object);
|
|
1144
|
+
const center = new THREE3.Vector3();
|
|
1145
|
+
const size = new THREE3.Vector3();
|
|
1146
|
+
bbox.getCenter(center);
|
|
1147
|
+
bbox.getSize(size);
|
|
1148
|
+
return { bbox, center, size };
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* 为指定车辆创建障碍物检测器
|
|
1152
|
+
*/
|
|
1153
|
+
_createObstacleCheckerFor(vehicleGroup, bbox, scale) {
|
|
1154
|
+
return {
|
|
1155
|
+
isBlocked: (start, end) => {
|
|
1156
|
+
const vehiclePos = vehicleGroup.position;
|
|
1157
|
+
const vehicleQuat = vehicleGroup.quaternion;
|
|
1158
|
+
const center = new THREE3.Vector3();
|
|
1159
|
+
const size = new THREE3.Vector3();
|
|
1160
|
+
bbox.getCenter(center);
|
|
1161
|
+
bbox.getSize(size);
|
|
1162
|
+
center.applyQuaternion(vehicleQuat).add(vehiclePos);
|
|
1163
|
+
const halfSize = size.clone().multiplyScalar(0.5 * scale);
|
|
1164
|
+
const corners = [];
|
|
1165
|
+
for (let x = -1; x <= 1; x += 2) {
|
|
1166
|
+
for (let y = -1; y <= 1; y += 2) {
|
|
1167
|
+
for (let z = -1; z <= 1; z += 2) {
|
|
1168
|
+
const localCorner = new THREE3.Vector3(
|
|
1169
|
+
halfSize.x * x,
|
|
1170
|
+
halfSize.y * y,
|
|
1171
|
+
halfSize.z * z
|
|
1172
|
+
);
|
|
1173
|
+
const worldCorner = localCorner.applyQuaternion(vehicleQuat).add(center);
|
|
1174
|
+
corners.push(worldCorner);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
const expandedBBox = new THREE3.Box3();
|
|
1179
|
+
corners.forEach((corner) => expandedBBox.expandByPoint(corner));
|
|
1180
|
+
expandedBBox.expandByScalar(100 * this.playerModel.scale);
|
|
1181
|
+
const direction = new THREE3.Vector3().subVectors(end, start);
|
|
1182
|
+
const length = direction.length();
|
|
1183
|
+
direction.normalize();
|
|
1184
|
+
const ray = new THREE3.Ray(start, direction);
|
|
1185
|
+
const intersection = new THREE3.Vector3();
|
|
1186
|
+
const intersects = ray.intersectBox(expandedBBox, intersection);
|
|
1187
|
+
return intersects !== null && start.distanceTo(intersection) < length;
|
|
1188
|
+
},
|
|
1189
|
+
getNavigationNodes: (start, _goal) => {
|
|
1190
|
+
const nodes = [];
|
|
1191
|
+
const vehiclePos = vehicleGroup.position;
|
|
1192
|
+
const vehicleQuat = vehicleGroup.quaternion;
|
|
1193
|
+
const vehicleForward = new THREE3.Vector3(
|
|
1194
|
+
0,
|
|
1195
|
+
0,
|
|
1196
|
+
1
|
|
1197
|
+
).applyQuaternion(vehicleQuat);
|
|
1198
|
+
const vehicleRight = new THREE3.Vector3(1, 0, 0).applyQuaternion(
|
|
1199
|
+
vehicleQuat
|
|
1200
|
+
);
|
|
1201
|
+
const bboxSize = new THREE3.Vector3();
|
|
1202
|
+
bbox.getSize(bboxSize);
|
|
1203
|
+
const halfLength = bboxSize.z / 2 * scale;
|
|
1204
|
+
const halfWidth = bboxSize.x / 2 * scale;
|
|
1205
|
+
const bypassMargin = 300 * this.playerModel.scale;
|
|
1206
|
+
const extendedMargin = 500 * this.playerModel.scale;
|
|
1207
|
+
const groundY = start.y;
|
|
1208
|
+
for (const margin of [bypassMargin, extendedMargin]) {
|
|
1209
|
+
nodes.push(
|
|
1210
|
+
vehiclePos.clone().add(
|
|
1211
|
+
vehicleForward.clone().multiplyScalar(halfLength + margin)
|
|
1212
|
+
).add(
|
|
1213
|
+
vehicleRight.clone().multiplyScalar(-halfWidth - margin)
|
|
1214
|
+
).setY(groundY)
|
|
1215
|
+
);
|
|
1216
|
+
nodes.push(
|
|
1217
|
+
vehiclePos.clone().add(
|
|
1218
|
+
vehicleForward.clone().multiplyScalar(halfLength + margin)
|
|
1219
|
+
).add(
|
|
1220
|
+
vehicleRight.clone().multiplyScalar(halfWidth + margin)
|
|
1221
|
+
).setY(groundY)
|
|
1222
|
+
);
|
|
1223
|
+
nodes.push(
|
|
1224
|
+
vehiclePos.clone().add(
|
|
1225
|
+
vehicleForward.clone().multiplyScalar(-halfLength - margin)
|
|
1226
|
+
).add(
|
|
1227
|
+
vehicleRight.clone().multiplyScalar(-halfWidth - margin)
|
|
1228
|
+
).setY(groundY)
|
|
1229
|
+
);
|
|
1230
|
+
nodes.push(
|
|
1231
|
+
vehiclePos.clone().add(
|
|
1232
|
+
vehicleForward.clone().multiplyScalar(-halfLength - margin)
|
|
1233
|
+
).add(
|
|
1234
|
+
vehicleRight.clone().multiplyScalar(halfWidth + margin)
|
|
1235
|
+
).setY(groundY)
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
return nodes;
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* 开关车门动画(操作当前 activeVehicle)
|
|
1244
|
+
*/
|
|
1245
|
+
openVehicleDoor(isOpen = true) {
|
|
1246
|
+
const v = this.activeVehicle;
|
|
1247
|
+
if (!v?.vehicleActions) return;
|
|
1248
|
+
const next = v.vehicleActions.get("openDoor");
|
|
1249
|
+
if (!next) return;
|
|
1250
|
+
const duration = next.getClip().duration;
|
|
1251
|
+
next.reset();
|
|
1252
|
+
next.setEffectiveWeight(1);
|
|
1253
|
+
if (isOpen) {
|
|
1254
|
+
next.setEffectiveTimeScale(duration * 2);
|
|
1255
|
+
next.time = 0;
|
|
1256
|
+
v.vehiclIsOpenDoor = true;
|
|
1257
|
+
} else {
|
|
1258
|
+
next.setEffectiveTimeScale(-duration * 2);
|
|
1259
|
+
next.time = duration;
|
|
1260
|
+
v.vehiclIsOpenDoor = false;
|
|
1261
|
+
}
|
|
1262
|
+
next.setLoop(THREE3.LoopOnce, 1);
|
|
1263
|
+
next.clampWhenFinished = true;
|
|
1264
|
+
next.play();
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* 上车:自动寻找最近的车辆
|
|
1268
|
+
*/
|
|
1269
|
+
enterVehicle() {
|
|
1270
|
+
if (this.vehicles.length === 0 || this.isMovingToBoardingPoint) return;
|
|
1271
|
+
let nearestVehicle = null;
|
|
1272
|
+
let nearestDist = Infinity;
|
|
1273
|
+
let nearBoardingPointWorld = null;
|
|
1274
|
+
for (const v2 of this.vehicles) {
|
|
1275
|
+
const boardingPointLocal = v2.boardingPoint.clone().multiplyScalar(v2.scale);
|
|
1276
|
+
const boardingPointWorld = new THREE3.Vector3();
|
|
1277
|
+
v2.vehicleGroup.localToWorld(
|
|
1278
|
+
boardingPointWorld.copy(boardingPointLocal)
|
|
1279
|
+
);
|
|
1280
|
+
const dist = this.player.position.distanceTo(boardingPointWorld);
|
|
1281
|
+
if (dist < 800 * this.playerModel.scale && dist < nearestDist) {
|
|
1282
|
+
nearestDist = dist;
|
|
1283
|
+
nearestVehicle = v2;
|
|
1284
|
+
nearBoardingPointWorld = boardingPointWorld;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
if (!nearestVehicle || !nearBoardingPointWorld) return;
|
|
1288
|
+
this.activeVehicle = nearestVehicle;
|
|
1289
|
+
const v = nearestVehicle;
|
|
1290
|
+
const vel = v.chassisBody.linvel();
|
|
1291
|
+
const horizSpeed = Math.sqrt(vel.x * vel.x + vel.z * vel.z);
|
|
1292
|
+
if (horizSpeed > 0.1) return;
|
|
1293
|
+
this.boardingPointWorld = nearBoardingPointWorld;
|
|
1294
|
+
const vehicleForward = new THREE3.Vector3(0, 0, 1).applyQuaternion(v.vehicleGroup.quaternion).normalize();
|
|
1295
|
+
const path = v.pathPlanner.findPath(
|
|
1296
|
+
this.player.position.clone(),
|
|
1297
|
+
this.boardingPointWorld
|
|
1298
|
+
);
|
|
1299
|
+
this.boardingWaypoints = path;
|
|
1300
|
+
this.currentWaypointIndex = 0;
|
|
1301
|
+
this.boardingTargetDir = vehicleForward;
|
|
1302
|
+
this.isMovingToBoardingPoint = true;
|
|
1303
|
+
this.playPersonAnimationByName("walking");
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* 走向上车点
|
|
1307
|
+
*/
|
|
1308
|
+
updateMoveToBoardingPoint(delta) {
|
|
1309
|
+
if (!this.isMovingToBoardingPoint || !this.boardingTargetDir || this.boardingWaypoints.length === 0) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
if (this.currentWaypointIndex >= this.boardingWaypoints.length) {
|
|
1313
|
+
this.finalizeBoarding(delta);
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const currentWaypoint = this.boardingWaypoints[this.currentWaypointIndex];
|
|
1317
|
+
const currentPos = this.player.position.clone();
|
|
1318
|
+
const horizontalDistance = new THREE3.Vector2(
|
|
1319
|
+
currentWaypoint.x - currentPos.x,
|
|
1320
|
+
currentWaypoint.z - currentPos.z
|
|
1321
|
+
).length();
|
|
1322
|
+
const isLastWaypoint = this.currentWaypointIndex === this.boardingWaypoints.length - 1;
|
|
1323
|
+
const waypointThreshold = isLastWaypoint ? 0 : 10 * this.playerModel.scale;
|
|
1324
|
+
if (horizontalDistance > waypointThreshold) {
|
|
1325
|
+
const moveDir = new THREE3.Vector3(
|
|
1326
|
+
currentWaypoint.x - currentPos.x,
|
|
1327
|
+
0,
|
|
1328
|
+
currentWaypoint.z - currentPos.z
|
|
1329
|
+
).normalize();
|
|
1330
|
+
const moveDistance = Math.min(
|
|
1331
|
+
this.boardingMoveSpeed * this.playerModel.scale * delta,
|
|
1332
|
+
horizontalDistance
|
|
1333
|
+
);
|
|
1334
|
+
this.player.position.add(moveDir.multiplyScalar(moveDistance));
|
|
1335
|
+
const lookTarget = this.player.position.clone().add(moveDir);
|
|
1336
|
+
this.targetMat.lookAt(
|
|
1337
|
+
this.player.position,
|
|
1338
|
+
lookTarget,
|
|
1339
|
+
this.player.up
|
|
1340
|
+
);
|
|
1341
|
+
this.targetQuat.setFromRotationMatrix(this.targetMat);
|
|
1342
|
+
this.targetQuat.multiply(this.flip180Quat);
|
|
1343
|
+
const rotateAlpha = Math.min(1, this.boardingRotateSpeed * delta);
|
|
1344
|
+
this.player.quaternion.slerp(this.targetQuat, rotateAlpha);
|
|
1345
|
+
} else {
|
|
1346
|
+
this.currentWaypointIndex++;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* 完成上车
|
|
1351
|
+
*/
|
|
1352
|
+
finalizeBoarding(delta) {
|
|
1353
|
+
const v = this.activeVehicle;
|
|
1354
|
+
if (!this.boardingTargetDir || !v || !this.isMovingToBoardingPoint) return;
|
|
1355
|
+
const currentDir = new THREE3.Vector3(0, 0, -1).applyQuaternion(this.player.quaternion).normalize();
|
|
1356
|
+
const targetDir = this.boardingTargetDir.clone().normalize();
|
|
1357
|
+
const angleDiff = currentDir.angleTo(targetDir);
|
|
1358
|
+
if (angleDiff > 0.01) {
|
|
1359
|
+
const lookTarget = this.player.position.clone().add(targetDir);
|
|
1360
|
+
this.targetMat.lookAt(
|
|
1361
|
+
this.player.position,
|
|
1362
|
+
lookTarget,
|
|
1363
|
+
this.player.up
|
|
1364
|
+
);
|
|
1365
|
+
this.targetQuat.setFromRotationMatrix(this.targetMat);
|
|
1366
|
+
const rotateAlpha = Math.min(1, this.boardingRotateSpeed * delta);
|
|
1367
|
+
this.player.quaternion.slerp(this.targetQuat, rotateAlpha);
|
|
1368
|
+
} else {
|
|
1369
|
+
this.boardingWaypoints = [];
|
|
1370
|
+
this.currentWaypointIndex = 0;
|
|
1371
|
+
this.boardingTargetDir = null;
|
|
1372
|
+
v.pathPlanner?.clearVisualization();
|
|
1373
|
+
this.playPersonAnimationByName("enterCar");
|
|
1374
|
+
this.isBoardingAnimPlaying = true;
|
|
1375
|
+
this.closeDoorTriggered = false;
|
|
1376
|
+
if (!v.vehiclIsOpenDoor) this.openVehicleDoor();
|
|
1377
|
+
this.player.rotation.copy(v.vehicleGroup.rotation);
|
|
1378
|
+
this.player.quaternion.multiply(this.flip180Quat);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
onEnterCarAnimFinished() {
|
|
1382
|
+
const v = this.activeVehicle;
|
|
1383
|
+
if (!v || !this.isMovingToBoardingPoint) return;
|
|
1384
|
+
this.player.updateMatrixWorld(true);
|
|
1385
|
+
const offsetY = this.boardingPointWorld.y - this.player.position.y;
|
|
1386
|
+
this.controllerMode = 1;
|
|
1387
|
+
this.syncControllerModeBtnEl();
|
|
1388
|
+
this.setOverShoulderView(false);
|
|
1389
|
+
v.vehicleGroup.attach(this.player);
|
|
1390
|
+
this.player.position.add(
|
|
1391
|
+
v.seatOffset.clone().multiplyScalar(v.scale).add(new THREE3.Vector3(0, offsetY, 0))
|
|
1392
|
+
);
|
|
1393
|
+
this.isMovingToBoardingPoint = false;
|
|
1394
|
+
}
|
|
518
1395
|
/**
|
|
519
|
-
*
|
|
1396
|
+
* 下车
|
|
520
1397
|
*/
|
|
1398
|
+
exitVehicle() {
|
|
1399
|
+
const v = this.activeVehicle;
|
|
1400
|
+
if (!v) return;
|
|
1401
|
+
this.isMovingToBoardingPoint = false;
|
|
1402
|
+
this.boardingWaypoints = [];
|
|
1403
|
+
this.currentWaypointIndex = 0;
|
|
1404
|
+
this.boardingTargetDir = null;
|
|
1405
|
+
const vel = v.chassisBody.linvel();
|
|
1406
|
+
const horizSpeed = Math.sqrt(vel.x * vel.x + vel.z * vel.z);
|
|
1407
|
+
const isStationary = horizSpeed < 0.1;
|
|
1408
|
+
if (isStationary) {
|
|
1409
|
+
this.playPersonAnimationByName("exitCar");
|
|
1410
|
+
this.isExitAnimPlaying = true;
|
|
1411
|
+
this.closeExitDoorTriggered = false;
|
|
1412
|
+
} else {
|
|
1413
|
+
this.playPersonAnimationByName("idle");
|
|
1414
|
+
}
|
|
1415
|
+
this.openVehicleDoor(true);
|
|
1416
|
+
this.controllerMode = 0;
|
|
1417
|
+
this.syncControllerModeBtnEl();
|
|
1418
|
+
this.setOverShoulderView(this.enableOverShoulderView);
|
|
1419
|
+
this.scene.attach(this.player);
|
|
1420
|
+
if (this.isFirstPerson) {
|
|
1421
|
+
this.setFirstPersonCamera();
|
|
1422
|
+
}
|
|
1423
|
+
this.setControllerTransition();
|
|
1424
|
+
}
|
|
1425
|
+
// ==================== 相机与视角控制 ====================
|
|
521
1426
|
changeView() {
|
|
522
1427
|
this.isFirstPerson = !this.isFirstPerson;
|
|
523
1428
|
if (this.isFirstPerson) {
|
|
524
|
-
this.
|
|
525
|
-
this.
|
|
526
|
-
this.camera.rotation.set(0, Math.PI, 0);
|
|
527
|
-
this.controls.enableZoom = false;
|
|
1429
|
+
this.setFirstPersonCamera();
|
|
1430
|
+
this.setOverShoulderView(false);
|
|
528
1431
|
} else {
|
|
1432
|
+
this.controls.enabled = true;
|
|
529
1433
|
this.scene.attach(this.camera);
|
|
530
1434
|
const worldPos = this.player.position.clone();
|
|
531
|
-
const dir = new
|
|
1435
|
+
const dir = new THREE3.Vector3(0, 0, -1).applyQuaternion(
|
|
1436
|
+
this.player.quaternion
|
|
1437
|
+
);
|
|
532
1438
|
const angle = Math.atan2(dir.z, dir.x);
|
|
533
|
-
const offset = new
|
|
1439
|
+
const offset = new THREE3.Vector3(
|
|
1440
|
+
Math.cos(angle) * 400 * this.playerModel.scale,
|
|
1441
|
+
200 * this.playerModel.scale,
|
|
1442
|
+
Math.sin(angle) * 400 * this.playerModel.scale
|
|
1443
|
+
);
|
|
534
1444
|
this.camera.position.copy(worldPos).add(offset);
|
|
535
1445
|
this.controls.target.copy(worldPos);
|
|
536
1446
|
this.controls.enableZoom = this.enableZoom;
|
|
1447
|
+
this.setOverShoulderView(this.enableOverShoulderView);
|
|
537
1448
|
}
|
|
538
1449
|
this.setPointerLock();
|
|
539
1450
|
}
|
|
540
|
-
|
|
541
|
-
this.
|
|
542
|
-
|
|
1451
|
+
setFirstPersonCamera() {
|
|
1452
|
+
this.controls.enabled = false;
|
|
1453
|
+
if (this.personHead) {
|
|
1454
|
+
this.personHead?.attach(this.camera);
|
|
1455
|
+
this.camera.position.set(0, 10, 20);
|
|
1456
|
+
} else {
|
|
1457
|
+
this.player.attach(this.camera);
|
|
1458
|
+
this.camera.position.set(
|
|
1459
|
+
0,
|
|
1460
|
+
40 * this.playerModel.scale,
|
|
1461
|
+
30 * this.playerModel.scale
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
this.camera.rotation.set(0, Math.PI, 0);
|
|
1465
|
+
this.controls.enableZoom = false;
|
|
543
1466
|
}
|
|
544
|
-
/**
|
|
545
|
-
* 设置指针锁定
|
|
546
|
-
*/
|
|
547
1467
|
setPointerLock() {
|
|
548
1468
|
if ((this.thirdMouseMode === 0 || this.thirdMouseMode === 1) && !this.isFirstPerson || this.isFirstPerson) {
|
|
549
1469
|
document.body.requestPointerLock();
|
|
@@ -551,33 +1471,38 @@ var PlayerController = class {
|
|
|
551
1471
|
document.exitPointerLock();
|
|
552
1472
|
}
|
|
553
1473
|
}
|
|
554
|
-
/**
|
|
555
|
-
* 设置摄像机初始位置
|
|
556
|
-
*/
|
|
557
1474
|
setCameraPos() {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
1475
|
+
requestAnimationFrame(() => {
|
|
1476
|
+
if (this.isFirstPerson) {
|
|
1477
|
+
this.person.add(this.camera);
|
|
1478
|
+
this.camera.position.set(
|
|
1479
|
+
0,
|
|
1480
|
+
40 * this.playerModel.scale,
|
|
1481
|
+
30 * this.playerModel.scale
|
|
1482
|
+
);
|
|
1483
|
+
} else {
|
|
1484
|
+
const worldPos = this.player.position.clone();
|
|
1485
|
+
const dir = new THREE3.Vector3(0, 0, -1).applyQuaternion(
|
|
1486
|
+
this.player.quaternion
|
|
1487
|
+
);
|
|
1488
|
+
const angle = Math.atan2(dir.z, dir.x);
|
|
1489
|
+
const offset = new THREE3.Vector3(
|
|
1490
|
+
Math.cos(angle) * 400 * this.playerModel.scale,
|
|
1491
|
+
200 * this.playerModel.scale,
|
|
1492
|
+
Math.sin(angle) * 400 * this.playerModel.scale
|
|
1493
|
+
);
|
|
1494
|
+
this.camera.position.copy(worldPos).add(offset);
|
|
1495
|
+
this.controls.enableZoom = this.enableZoom;
|
|
1496
|
+
}
|
|
1497
|
+
this.camera.updateProjectionMatrix();
|
|
1498
|
+
});
|
|
568
1499
|
}
|
|
569
|
-
/**
|
|
570
|
-
* 设置控制器
|
|
571
|
-
*/
|
|
572
1500
|
setControls() {
|
|
573
1501
|
this.controls.enableZoom = this.enableZoom;
|
|
574
1502
|
this.controls.rotateSpeed = this.mouseSensity * 0.05;
|
|
575
1503
|
this.controls.maxPolarAngle = Math.PI * (300 / 360);
|
|
576
1504
|
this.controls.mouseButtons = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
|
|
577
1505
|
}
|
|
578
|
-
/**
|
|
579
|
-
* 重置控制器
|
|
580
|
-
*/
|
|
581
1506
|
resetControls() {
|
|
582
1507
|
if (!this.controls) return;
|
|
583
1508
|
this.controls.enabled = true;
|
|
@@ -587,38 +1512,80 @@ var PlayerController = class {
|
|
|
587
1512
|
this.controls.enableZoom = true;
|
|
588
1513
|
this.controls.mouseButtons = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
|
|
589
1514
|
}
|
|
590
|
-
/**
|
|
591
|
-
* 设置朝向
|
|
592
|
-
*/
|
|
593
1515
|
setToward(dx, dy, speed) {
|
|
594
|
-
if (this.
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
1516
|
+
if (this.controllerMode == 0) {
|
|
1517
|
+
if (this.isFirstPerson) {
|
|
1518
|
+
if (this.isMovingToBoardingPoint) return;
|
|
1519
|
+
const yaw = -dx * speed * this.mouseSensity;
|
|
1520
|
+
const pitch = -dy * speed * this.mouseSensity;
|
|
1521
|
+
this.player.rotateY(yaw);
|
|
1522
|
+
this.camera.rotation.x = THREE3.MathUtils.clamp(
|
|
1523
|
+
this.camera.rotation.x + pitch,
|
|
1524
|
+
-1.1,
|
|
1525
|
+
1.4
|
|
1526
|
+
);
|
|
1527
|
+
} else {
|
|
1528
|
+
const sensitivity = this.mouseSensity;
|
|
1529
|
+
const deltaX = -dx * speed * sensitivity;
|
|
1530
|
+
const deltaY = -dy * speed * sensitivity;
|
|
1531
|
+
const target = this.player.position.clone();
|
|
1532
|
+
const distance = this.camera.position.distanceTo(target);
|
|
1533
|
+
const currentPosition = this.camera.position.clone().sub(target);
|
|
1534
|
+
let theta = Math.atan2(currentPosition.x, currentPosition.z);
|
|
1535
|
+
let phi = Math.acos(currentPosition.y / distance);
|
|
1536
|
+
theta += deltaX;
|
|
1537
|
+
phi += deltaY;
|
|
1538
|
+
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));
|
|
1539
|
+
const newX = distance * Math.sin(phi) * Math.sin(theta);
|
|
1540
|
+
const newY = distance * Math.cos(phi);
|
|
1541
|
+
const newZ = distance * Math.sin(phi) * Math.cos(theta);
|
|
1542
|
+
this.camera.position.set(
|
|
1543
|
+
target.x + newX,
|
|
1544
|
+
target.y + newY,
|
|
1545
|
+
target.z + newZ
|
|
1546
|
+
);
|
|
1547
|
+
this.camera.lookAt(target);
|
|
1548
|
+
}
|
|
599
1549
|
} else {
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
1550
|
+
const v = this.activeVehicle;
|
|
1551
|
+
if (!v) return;
|
|
1552
|
+
if (this.isFirstPerson) {
|
|
1553
|
+
const yaw = -dx * speed * this.mouseSensity;
|
|
1554
|
+
const pitch = -dy * speed * this.mouseSensity;
|
|
1555
|
+
this.camera.rotation.y = THREE3.MathUtils.clamp(
|
|
1556
|
+
this.camera.rotation.y + yaw,
|
|
1557
|
+
Math.PI * (3 / 4),
|
|
1558
|
+
Math.PI * (5 / 4)
|
|
1559
|
+
);
|
|
1560
|
+
this.camera.rotation.x = THREE3.MathUtils.clamp(
|
|
1561
|
+
this.camera.rotation.x + pitch,
|
|
1562
|
+
0,
|
|
1563
|
+
Math.PI * (1 / 3)
|
|
1564
|
+
);
|
|
1565
|
+
} else {
|
|
1566
|
+
const sensitivity = this.mouseSensity;
|
|
1567
|
+
const deltaX = -dx * speed * sensitivity;
|
|
1568
|
+
const deltaY = -dy * speed * sensitivity;
|
|
1569
|
+
const target = v.vehicleGroup.position.clone();
|
|
1570
|
+
const distance = this.camera.position.distanceTo(target);
|
|
1571
|
+
const currentPosition = this.camera.position.clone().sub(target);
|
|
1572
|
+
let theta = Math.atan2(currentPosition.x, currentPosition.z);
|
|
1573
|
+
let phi = Math.acos(currentPosition.y / distance);
|
|
1574
|
+
theta += deltaX;
|
|
1575
|
+
phi += deltaY;
|
|
1576
|
+
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));
|
|
1577
|
+
const newX = distance * Math.sin(phi) * Math.sin(theta);
|
|
1578
|
+
const newY = distance * Math.cos(phi);
|
|
1579
|
+
const newZ = distance * Math.sin(phi) * Math.cos(theta);
|
|
1580
|
+
this.camera.position.set(
|
|
1581
|
+
target.x + newX,
|
|
1582
|
+
target.y + newY,
|
|
1583
|
+
target.z + newZ
|
|
1584
|
+
);
|
|
1585
|
+
this.camera.lookAt(target);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
622
1589
|
unifiedAttribute(collected) {
|
|
623
1590
|
const attrMap = /* @__PURE__ */ new Map();
|
|
624
1591
|
const attrConflict = /* @__PURE__ */ new Set();
|
|
@@ -638,7 +1605,12 @@ var PlayerController = class {
|
|
|
638
1605
|
const itemSize = attr.itemSize;
|
|
639
1606
|
const normalized = attr.normalized;
|
|
640
1607
|
if (!attrMap.has(name)) {
|
|
641
|
-
attrMap.set(name, {
|
|
1608
|
+
attrMap.set(name, {
|
|
1609
|
+
itemSize,
|
|
1610
|
+
arrayCtor: ctor,
|
|
1611
|
+
examples: 1,
|
|
1612
|
+
normalized
|
|
1613
|
+
});
|
|
642
1614
|
} else {
|
|
643
1615
|
const m = attrMap.get(name);
|
|
644
1616
|
if (m.itemSize !== itemSize || m.arrayCtor !== ctor || m.normalized !== normalized) {
|
|
@@ -665,28 +1637,21 @@ var PlayerController = class {
|
|
|
665
1637
|
const meta = attrMap.get(name);
|
|
666
1638
|
const len = count * meta.itemSize;
|
|
667
1639
|
const array = new meta.arrayCtor(len);
|
|
668
|
-
g.setAttribute(
|
|
1640
|
+
g.setAttribute(
|
|
1641
|
+
name,
|
|
1642
|
+
new THREE3.BufferAttribute(
|
|
1643
|
+
array,
|
|
1644
|
+
meta.itemSize,
|
|
1645
|
+
meta.normalized
|
|
1646
|
+
)
|
|
1647
|
+
);
|
|
669
1648
|
}
|
|
670
1649
|
}
|
|
671
1650
|
}
|
|
672
1651
|
return collected;
|
|
673
1652
|
}
|
|
674
|
-
/**
|
|
675
|
-
* BVH碰撞体构建
|
|
676
|
-
*/
|
|
677
1653
|
async createBVH(meshUrl = "") {
|
|
678
1654
|
await this.initLoader();
|
|
679
|
-
const ensureAttributesMinimal = (geom) => {
|
|
680
|
-
if (!geom.attributes.position) return null;
|
|
681
|
-
if (!geom.attributes.normal) geom.computeVertexNormals();
|
|
682
|
-
if (!geom.attributes.uv) {
|
|
683
|
-
const count = geom.attributes.position.count;
|
|
684
|
-
const dummyUV = new Float32Array(count * 2);
|
|
685
|
-
geom.setAttribute("uv", new THREE.BufferAttribute(dummyUV, 2));
|
|
686
|
-
}
|
|
687
|
-
return geom;
|
|
688
|
-
};
|
|
689
|
-
let collected = [];
|
|
690
1655
|
if (meshUrl === "") {
|
|
691
1656
|
if (this.collider) {
|
|
692
1657
|
this.scene.remove(this.collider);
|
|
@@ -699,68 +1664,435 @@ var PlayerController = class {
|
|
|
699
1664
|
let geom = mesh.geometry.clone();
|
|
700
1665
|
geom.applyMatrix4(mesh.matrixWorld);
|
|
701
1666
|
if (geom.index) geom = geom.toNonIndexed();
|
|
702
|
-
const safe = ensureAttributesMinimal(geom);
|
|
703
|
-
if (safe) collected.push(safe);
|
|
1667
|
+
const safe = this.ensureAttributesMinimal(geom);
|
|
1668
|
+
if (safe) this.collected.push(safe);
|
|
704
1669
|
} catch (e) {
|
|
705
1670
|
console.warn("\u5904\u7406\u7F51\u683C\u65F6\u51FA\u9519\uFF1A", mesh, e);
|
|
706
1671
|
}
|
|
707
1672
|
}
|
|
708
1673
|
});
|
|
709
|
-
if (!collected.length) return;
|
|
710
|
-
collected = this.unifiedAttribute(collected);
|
|
1674
|
+
if (!this.collected.length) return;
|
|
1675
|
+
this.collected = this.unifiedAttribute(this.collected);
|
|
711
1676
|
} else {
|
|
712
1677
|
const gltf = await this.loader.loadAsync(meshUrl);
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1678
|
+
const obj = gltf.scene.children[0];
|
|
1679
|
+
if (obj && obj?.geometry) {
|
|
1680
|
+
const mesh = obj;
|
|
1681
|
+
let geom = mesh.geometry.clone();
|
|
1682
|
+
geom.applyMatrix4(mesh.matrixWorld);
|
|
1683
|
+
if (geom.index) geom = geom.toNonIndexed();
|
|
1684
|
+
const safe = this.ensureAttributesMinimal(geom);
|
|
1685
|
+
if (safe) this.collected.push(safe);
|
|
1686
|
+
} else {
|
|
1687
|
+
obj.traverse((c) => {
|
|
1688
|
+
const mesh = c;
|
|
1689
|
+
if (mesh?.isMesh && mesh.geometry && c.name !== "capsule") {
|
|
1690
|
+
try {
|
|
1691
|
+
let geom = mesh.geometry.clone();
|
|
1692
|
+
geom.applyMatrix4(mesh.matrixWorld);
|
|
1693
|
+
if (geom.index) geom = geom.toNonIndexed();
|
|
1694
|
+
const safe = this.ensureAttributesMinimal(geom);
|
|
1695
|
+
if (safe) this.collected.push(safe);
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
console.warn("\u5904\u7406\u7F51\u683C\u65F6\u51FA\u9519\uFF1A", mesh, e);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
if (!this.collected.length) return;
|
|
1702
|
+
this.collected = this.unifiedAttribute(this.collected);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
const merged = BufferGeometryUtils.mergeGeometries(
|
|
1706
|
+
this.collected,
|
|
1707
|
+
false
|
|
1708
|
+
);
|
|
722
1709
|
if (!merged) {
|
|
723
1710
|
console.error("\u5408\u5E76\u51E0\u4F55\u5931\u8D25");
|
|
724
1711
|
return;
|
|
725
1712
|
}
|
|
726
1713
|
merged.boundsTree = new MeshBVH(merged, { maxDepth: 100 });
|
|
727
|
-
this.collider = new
|
|
1714
|
+
this.collider = new THREE3.Mesh(
|
|
728
1715
|
merged,
|
|
729
|
-
new
|
|
1716
|
+
new THREE3.MeshBasicMaterial({
|
|
730
1717
|
opacity: 0.5,
|
|
731
1718
|
transparent: true,
|
|
732
|
-
wireframe: true
|
|
1719
|
+
wireframe: true,
|
|
1720
|
+
depthTest: true
|
|
733
1721
|
})
|
|
734
1722
|
);
|
|
735
1723
|
if (this.displayCollider) this.scene.add(this.collider);
|
|
736
1724
|
if (this.displayVisualizer) {
|
|
737
1725
|
if (this.visualizer) this.scene.remove(this.visualizer);
|
|
738
|
-
this.visualizer = new MeshBVHHelper(this.collider,
|
|
1726
|
+
this.visualizer = new MeshBVHHelper(this.collider, 0);
|
|
739
1727
|
this.scene.add(this.visualizer);
|
|
740
1728
|
}
|
|
741
1729
|
this.boundingBoxMinY = this.collider.geometry.boundingBox.min.y;
|
|
742
1730
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1731
|
+
createDynamicBVH(objects = []) {
|
|
1732
|
+
if (this.dynamicCollider) {
|
|
1733
|
+
this.scene.remove(this.dynamicCollider);
|
|
1734
|
+
this.dynamicCollider = null;
|
|
1735
|
+
}
|
|
1736
|
+
this.dynamicCollected = [];
|
|
1737
|
+
objects.forEach((object) => {
|
|
1738
|
+
object.traverse((c) => {
|
|
1739
|
+
const mesh = c;
|
|
1740
|
+
if (mesh?.isMesh && mesh.geometry && c.name !== "capsule") {
|
|
1741
|
+
try {
|
|
1742
|
+
let geom = mesh.geometry.clone();
|
|
1743
|
+
geom.applyMatrix4(mesh.matrixWorld);
|
|
1744
|
+
if (geom.index) geom = geom.toNonIndexed();
|
|
1745
|
+
const safe = this.ensureAttributesMinimal(geom);
|
|
1746
|
+
if (safe) this.dynamicCollected.push(safe);
|
|
1747
|
+
} catch (e) {
|
|
1748
|
+
console.warn("\u5904\u7406\u7F51\u683C\u65F6\u51FA\u9519\uFF1A", mesh, e);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
});
|
|
1753
|
+
if (!this.dynamicCollected.length) return;
|
|
1754
|
+
this.dynamicCollected = this.unifiedAttribute(this.dynamicCollected);
|
|
1755
|
+
const merged = BufferGeometryUtils.mergeGeometries(
|
|
1756
|
+
this.dynamicCollected,
|
|
1757
|
+
false
|
|
1758
|
+
);
|
|
1759
|
+
if (!merged) {
|
|
1760
|
+
console.error("\u5408\u5E76\u51E0\u4F55\u5931\u8D25");
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
merged.boundsTree = new MeshBVH(merged);
|
|
1764
|
+
this.dynamicCollider = new THREE3.Mesh(
|
|
1765
|
+
merged,
|
|
1766
|
+
new THREE3.MeshBasicMaterial({
|
|
1767
|
+
opacity: 0.5,
|
|
1768
|
+
transparent: true,
|
|
1769
|
+
wireframe: true,
|
|
1770
|
+
depthTest: true
|
|
1771
|
+
})
|
|
1772
|
+
);
|
|
1773
|
+
if (this.displayCollider) this.scene.add(this.dynamicCollider);
|
|
1774
|
+
}
|
|
746
1775
|
getAngleWithYAxis(normal) {
|
|
747
1776
|
const yAxis = { x: 0, y: 1, z: 0 };
|
|
748
1777
|
const dotProduct = normal.x * yAxis.x + normal.y * yAxis.y + normal.z * yAxis.z;
|
|
749
|
-
const normalMagnitude = Math.sqrt(
|
|
1778
|
+
const normalMagnitude = Math.sqrt(
|
|
1779
|
+
normal.x * normal.x + normal.y * normal.y + normal.z * normal.z
|
|
1780
|
+
);
|
|
750
1781
|
const cosTheta = dotProduct / normalMagnitude;
|
|
751
1782
|
return Math.acos(cosTheta);
|
|
752
1783
|
}
|
|
1784
|
+
// ==================== 设置控制器过渡 ====================
|
|
1785
|
+
setControllerTransition() {
|
|
1786
|
+
if (this.isChangeControllerTransitionTimer) {
|
|
1787
|
+
clearTimeout(this.isChangeControllerTransitionTimer);
|
|
1788
|
+
this.isChangeControllerTransitionTimer = null;
|
|
1789
|
+
}
|
|
1790
|
+
let vGroups = [];
|
|
1791
|
+
for (const v of this.vehicles) {
|
|
1792
|
+
vGroups.push(v.vehicleGroup);
|
|
1793
|
+
}
|
|
1794
|
+
this.createDynamicBVH(vGroups);
|
|
1795
|
+
this.isChangeControllerTransitionTimer = setTimeout(() => {
|
|
1796
|
+
this.isChangeControllerTransitionTimer = null;
|
|
1797
|
+
for (const v of this.vehicles) {
|
|
1798
|
+
this.clearVehicleVelocity(v);
|
|
1799
|
+
}
|
|
1800
|
+
this.createDynamicBVH(vGroups);
|
|
1801
|
+
}, 3e3);
|
|
1802
|
+
}
|
|
1803
|
+
// 清除车辆速度
|
|
1804
|
+
clearVehicleVelocity(v) {
|
|
1805
|
+
if (!v || !this.world || !this.RAPIER) return;
|
|
1806
|
+
const { chassisBody, vehicleController } = v;
|
|
1807
|
+
const ZERO = new this.RAPIER.Vector3(0, 0, 0);
|
|
1808
|
+
chassisBody.setLinvel(ZERO, true);
|
|
1809
|
+
chassisBody.setAngvel(ZERO, true);
|
|
1810
|
+
const BIG_BRAKE = 1e6;
|
|
1811
|
+
for (let i = 0; i < 4; i++) {
|
|
1812
|
+
vehicleController.setWheelEngineForce(i, 0);
|
|
1813
|
+
vehicleController.setWheelBrake(i, BIG_BRAKE);
|
|
1814
|
+
}
|
|
1815
|
+
vehicleController.updateVehicle(1 / 60);
|
|
1816
|
+
this.world.timestep = 1 / 60;
|
|
1817
|
+
this.world.step();
|
|
1818
|
+
chassisBody.setLinvel(ZERO, true);
|
|
1819
|
+
chassisBody.setAngvel(ZERO, true);
|
|
1820
|
+
for (let i = 0; i < 4; i++) {
|
|
1821
|
+
vehicleController.setWheelBrake(i, 0);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
753
1824
|
// ==================== 循环更新 ====================
|
|
754
|
-
/**
|
|
755
|
-
* 每帧更新
|
|
756
|
-
*/
|
|
757
1825
|
async update(delta = clock.getDelta()) {
|
|
758
1826
|
if (!this.isupdate || !this.player || !this.collider) return;
|
|
759
|
-
delta = Math.min(delta, 1 /
|
|
1827
|
+
delta = Math.min(delta, 1 / 40);
|
|
1828
|
+
if (this.controllerMode == 1) {
|
|
1829
|
+
this.updateVehicle(delta);
|
|
1830
|
+
} else {
|
|
1831
|
+
this.updatePlayer(delta);
|
|
1832
|
+
if (this.isChangeControllerTransitionTimer)
|
|
1833
|
+
this.updateVehicleInertia(delta);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* 更新当前驾驶的车辆
|
|
1838
|
+
*/
|
|
1839
|
+
updateVehicle(delta) {
|
|
1840
|
+
const v = this.activeVehicle;
|
|
1841
|
+
if (!v || !this.world) return;
|
|
1842
|
+
const { vehicleController, chassisBody, vehicleGroup } = v;
|
|
1843
|
+
const rotation = chassisBody.rotation();
|
|
1844
|
+
const quat = new THREE3.Quaternion(
|
|
1845
|
+
rotation.x,
|
|
1846
|
+
rotation.y,
|
|
1847
|
+
rotation.z,
|
|
1848
|
+
rotation.w
|
|
1849
|
+
);
|
|
1850
|
+
const forward = new THREE3.Vector3(1, 0, 0).applyQuaternion(quat);
|
|
1851
|
+
const slopeAngle = Math.asin(forward.y);
|
|
1852
|
+
let factor = 1;
|
|
1853
|
+
if (slopeAngle < -0.05 && this.fwdPressed) factor = -Math.sin(slopeAngle) * 10;
|
|
1854
|
+
const accelerateForce = this.vehicleParams.power.accelerateForce * v.speedMultiplier;
|
|
1855
|
+
const maxSpeed = this.vehicleParams.power.maxSpeed * v.speedMultiplier;
|
|
1856
|
+
const engineForce = (Number(this.fwdPressed) * accelerateForce - Number(this.bkdPressed) * accelerateForce) * factor;
|
|
1857
|
+
vehicleController.setWheelEngineForce(0, engineForce);
|
|
1858
|
+
vehicleController.setWheelEngineForce(1, engineForce);
|
|
1859
|
+
vehicleController.setWheelEngineForce(2, engineForce);
|
|
1860
|
+
vehicleController.setWheelEngineForce(3, engineForce);
|
|
1861
|
+
const wheelBrake = Number(this.spacePressed) * this.vehicleParams.power.brakeForce * delta;
|
|
1862
|
+
vehicleController.setWheelBrake(0, wheelBrake);
|
|
1863
|
+
vehicleController.setWheelBrake(1, wheelBrake);
|
|
1864
|
+
vehicleController.setWheelBrake(2, wheelBrake);
|
|
1865
|
+
vehicleController.setWheelBrake(3, wheelBrake);
|
|
1866
|
+
const currentSteering = vehicleController.wheelSteering(0) || 0;
|
|
1867
|
+
const steerDirection = Number(this.lftPressed) - Number(this.rgtPressed);
|
|
1868
|
+
let steerSpeed;
|
|
1869
|
+
if (steerDirection === 0) {
|
|
1870
|
+
steerSpeed = this.vehicleParams.steering.steerReturnSpeed || 0.15;
|
|
1871
|
+
} else {
|
|
1872
|
+
steerSpeed = this.vehicleParams.steering.steerSpeed || 0.08;
|
|
1873
|
+
}
|
|
1874
|
+
const steerLerpFactor = 1 - Math.pow(1 - steerSpeed, delta);
|
|
1875
|
+
const targetSteering = this.vehicleParams.steering.maxSteerAngle * steerDirection;
|
|
1876
|
+
const steering = THREE3.MathUtils.lerp(
|
|
1877
|
+
currentSteering,
|
|
1878
|
+
targetSteering,
|
|
1879
|
+
steerLerpFactor
|
|
1880
|
+
);
|
|
1881
|
+
vehicleController.setWheelSteering(0, steering);
|
|
1882
|
+
vehicleController.setWheelSteering(1, steering);
|
|
1883
|
+
if ((this.rgtPressed || this.lftPressed) && this.shiftPressed) {
|
|
1884
|
+
vehicleController.setWheelSideFrictionStiffness(2, 0.5);
|
|
1885
|
+
vehicleController.setWheelSideFrictionStiffness(3, 0.5);
|
|
1886
|
+
} else {
|
|
1887
|
+
vehicleController.setWheelSideFrictionStiffness(2, 2);
|
|
1888
|
+
vehicleController.setWheelSideFrictionStiffness(3, 2);
|
|
1889
|
+
}
|
|
1890
|
+
this.updateVehicleInertia(delta);
|
|
1891
|
+
if (!this.isFirstPerson) {
|
|
1892
|
+
const lookTarget = vehicleGroup.position.clone();
|
|
1893
|
+
this.camera.position.sub(this.controls.target);
|
|
1894
|
+
this.controls.target.copy(lookTarget);
|
|
1895
|
+
this.camera.position.add(lookTarget);
|
|
1896
|
+
this.controls.update();
|
|
1897
|
+
const velocity = chassisBody.linvel();
|
|
1898
|
+
const currentSpeed = Math.sqrt(
|
|
1899
|
+
velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z
|
|
1900
|
+
);
|
|
1901
|
+
const speedRatio = Math.min(currentSpeed / maxSpeed, 1);
|
|
1902
|
+
const baseCamDistance = v.size.l * 0.8;
|
|
1903
|
+
const maxCamDistanceLimit = v.size.l * 5;
|
|
1904
|
+
const targetDistance = THREE3.MathUtils.lerp(
|
|
1905
|
+
baseCamDistance,
|
|
1906
|
+
maxCamDistanceLimit,
|
|
1907
|
+
speedRatio
|
|
1908
|
+
);
|
|
1909
|
+
this._personToCam.subVectors(
|
|
1910
|
+
this.camera.position,
|
|
1911
|
+
vehicleGroup.position
|
|
1912
|
+
);
|
|
1913
|
+
const origin = vehicleGroup.position.clone().add(new THREE3.Vector3(0, 0, 0));
|
|
1914
|
+
const direction = this._personToCam.clone().normalize();
|
|
1915
|
+
const desiredDist = targetDistance;
|
|
1916
|
+
this._raycasterPersonToCam.set(origin, direction);
|
|
1917
|
+
this._raycasterPersonToCam.far = desiredDist;
|
|
1918
|
+
const intersects = this._raycasterPersonToCam.intersectObject(
|
|
1919
|
+
this.collider,
|
|
1920
|
+
false
|
|
1921
|
+
);
|
|
1922
|
+
if (intersects.length > 0) {
|
|
1923
|
+
const hit = intersects[0];
|
|
1924
|
+
const safeDist = Math.max(
|
|
1925
|
+
hit.distance - this._camEpsilon,
|
|
1926
|
+
this.minCamDistance
|
|
1927
|
+
);
|
|
1928
|
+
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
1929
|
+
this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
|
|
1930
|
+
} else {
|
|
1931
|
+
this._raycasterPersonToCam.far = maxCamDistanceLimit;
|
|
1932
|
+
const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
|
|
1933
|
+
this.collider,
|
|
1934
|
+
false
|
|
1935
|
+
);
|
|
1936
|
+
let safeDist = desiredDist;
|
|
1937
|
+
if (intersectsMaxDis.length) {
|
|
1938
|
+
const hitMax = intersectsMaxDis[0];
|
|
1939
|
+
safeDist = Math.min(
|
|
1940
|
+
desiredDist,
|
|
1941
|
+
hitMax.distance - this._camEpsilon
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
1945
|
+
this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
|
|
1946
|
+
}
|
|
1947
|
+
if ((this.fwdPressed || this.bkdPressed) && this.vehicleParams.followVehicleDirection) {
|
|
1948
|
+
const vel = chassisBody.linvel();
|
|
1949
|
+
const velHorizontal = new THREE3.Vector3(vel.x, vel.y, vel.z);
|
|
1950
|
+
const velSpeed = velHorizontal.length();
|
|
1951
|
+
if (velSpeed > 0.3) {
|
|
1952
|
+
const targetBehindDir = velHorizontal.clone().normalize().negate();
|
|
1953
|
+
this.camBehindDir.lerp(targetBehindDir, this._camCollisionLerp).normalize();
|
|
1954
|
+
const camHeightOffset = v.size.h;
|
|
1955
|
+
const targetCamPos = lookTarget.clone().add(
|
|
1956
|
+
this.camBehindDir.clone().multiplyScalar(desiredDist)
|
|
1957
|
+
).add(new THREE3.Vector3(0, camHeightOffset, 0));
|
|
1958
|
+
this.camera.position.lerp(
|
|
1959
|
+
targetCamPos,
|
|
1960
|
+
this._camCollisionLerp
|
|
1961
|
+
);
|
|
1962
|
+
this.controls.update();
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
const vehicleUp = this.upVector.clone().applyQuaternion(vehicleGroup.quaternion);
|
|
1967
|
+
const angleWithUp = vehicleUp.angleTo(this.upVector);
|
|
1968
|
+
if (angleWithUp > Math.PI / 2) {
|
|
1969
|
+
const size = new THREE3.Vector3();
|
|
1970
|
+
v.vehicleBBox?.getSize(size);
|
|
1971
|
+
const translation2 = chassisBody.translation();
|
|
1972
|
+
chassisBody.setTranslation(
|
|
1973
|
+
new this.RAPIER.Vector3(
|
|
1974
|
+
translation2.x,
|
|
1975
|
+
translation2.y + size.y,
|
|
1976
|
+
translation2.z
|
|
1977
|
+
),
|
|
1978
|
+
true
|
|
1979
|
+
);
|
|
1980
|
+
chassisBody.setRotation(
|
|
1981
|
+
new this.RAPIER.Quaternion(0, 0, 0, 1),
|
|
1982
|
+
true
|
|
1983
|
+
);
|
|
1984
|
+
chassisBody.setLinvel(new this.RAPIER.Vector3(0, 0, 0), true);
|
|
1985
|
+
chassisBody.setAngvel(new this.RAPIER.Vector3(0, 0, 0), true);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* 更新所有车辆物理和位置
|
|
1990
|
+
*/
|
|
1991
|
+
updateVehicleInertia(delta) {
|
|
1992
|
+
if (!this.world) return;
|
|
1993
|
+
this.world.timestep = delta;
|
|
1994
|
+
this.world.step();
|
|
1995
|
+
for (const v of this.vehicles) {
|
|
1996
|
+
const { vehicleController, chassisBody, vehicleGroup, updateWheelVisuals } = v;
|
|
1997
|
+
vehicleController.updateVehicle(delta);
|
|
1998
|
+
if (chassisBody.isSleeping()) continue;
|
|
1999
|
+
const velocity = chassisBody.linvel();
|
|
2000
|
+
const currentSpeed = Math.sqrt(
|
|
2001
|
+
velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z
|
|
2002
|
+
);
|
|
2003
|
+
const maxSpeed = this.vehicleParams.power.maxSpeed * v.speedMultiplier;
|
|
2004
|
+
if (currentSpeed > maxSpeed) {
|
|
2005
|
+
const s = maxSpeed / currentSpeed;
|
|
2006
|
+
chassisBody.setLinvel(
|
|
2007
|
+
new this.RAPIER.Vector3(
|
|
2008
|
+
velocity.x * s,
|
|
2009
|
+
velocity.y * s,
|
|
2010
|
+
velocity.z * s
|
|
2011
|
+
),
|
|
2012
|
+
true
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
const translation = chassisBody.translation();
|
|
2016
|
+
const rotationSync = chassisBody.rotation();
|
|
2017
|
+
vehicleGroup.position.set(
|
|
2018
|
+
translation.x,
|
|
2019
|
+
translation.y,
|
|
2020
|
+
translation.z
|
|
2021
|
+
);
|
|
2022
|
+
vehicleGroup.quaternion.set(
|
|
2023
|
+
rotationSync.x,
|
|
2024
|
+
rotationSync.y,
|
|
2025
|
+
rotationSync.z,
|
|
2026
|
+
rotationSync.w
|
|
2027
|
+
);
|
|
2028
|
+
if (updateWheelVisuals) updateWheelVisuals();
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* 设置人物缩放
|
|
2033
|
+
*/
|
|
2034
|
+
setPlayerScale(newScale) {
|
|
2035
|
+
if (newScale <= 0) return;
|
|
2036
|
+
const ratio = newScale / this.playerModel.scale;
|
|
2037
|
+
this.playerModel.scale = newScale;
|
|
2038
|
+
this.gravity *= ratio;
|
|
2039
|
+
this.jumpHeight *= ratio;
|
|
2040
|
+
this.playerSpeed *= ratio;
|
|
2041
|
+
this.playerFlySpeed *= ratio;
|
|
2042
|
+
this.curPlayerSpeed *= ratio;
|
|
2043
|
+
this._camEpsilon *= ratio;
|
|
2044
|
+
this.minCamDistance *= ratio;
|
|
2045
|
+
this.maxCamDistance *= ratio;
|
|
2046
|
+
this.orginMaxCamDistance *= ratio;
|
|
2047
|
+
if (this.isFirstPerson) this.scene.attach(this.camera);
|
|
2048
|
+
this.player?.scale.multiplyScalar(ratio);
|
|
2049
|
+
if (this.player?.capsuleInfo) this.player.capsuleInfo.radius *= ratio;
|
|
2050
|
+
if (this.isFirstPerson) this.setFirstPersonCamera();
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* 更新人物
|
|
2054
|
+
*/
|
|
2055
|
+
updatePlayer(delta) {
|
|
2056
|
+
if (this.isMovingToBoardingPoint) {
|
|
2057
|
+
this.updateMoveToBoardingPoint(delta);
|
|
2058
|
+
}
|
|
760
2059
|
if (!this.isFlying) {
|
|
761
2060
|
this.player.position.addScaledVector(this.playerVelocity, delta);
|
|
762
2061
|
}
|
|
2062
|
+
if (this.isBoardingAnimPlaying) {
|
|
2063
|
+
const action = this.personActions?.get("enterCar");
|
|
2064
|
+
const duration = action.getClip().duration;
|
|
2065
|
+
const timeScale = action.getEffectiveTimeScale();
|
|
2066
|
+
const remaining = (duration - action.time) / timeScale * 1e3;
|
|
2067
|
+
if (!this.closeDoorTriggered && remaining <= 500) {
|
|
2068
|
+
this.closeDoorTriggered = true;
|
|
2069
|
+
this.openVehicleDoor(false);
|
|
2070
|
+
}
|
|
2071
|
+
if (action.time >= duration) {
|
|
2072
|
+
this.isBoardingAnimPlaying = false;
|
|
2073
|
+
this.closeDoorTriggered = false;
|
|
2074
|
+
this.onEnterCarAnimFinished();
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
if (this.isExitAnimPlaying) {
|
|
2079
|
+
const action = this.personActions?.get("exitCar");
|
|
2080
|
+
if (action) {
|
|
2081
|
+
const duration = action.getClip().duration;
|
|
2082
|
+
const timeScale = action.getEffectiveTimeScale();
|
|
2083
|
+
const remaining = (duration - action.time) / timeScale * 1e3;
|
|
2084
|
+
if (!this.closeExitDoorTriggered && remaining <= 500) {
|
|
2085
|
+
this.closeExitDoorTriggered = true;
|
|
2086
|
+
this.openVehicleDoor(false);
|
|
2087
|
+
}
|
|
2088
|
+
if (action.time >= duration) {
|
|
2089
|
+
this.isExitAnimPlaying = false;
|
|
2090
|
+
this.closeExitDoorTriggered = false;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
763
2094
|
this.updateMixers(delta);
|
|
2095
|
+
if (this.controllerMode === 1) return;
|
|
764
2096
|
this.camera.getWorldDirection(this.camDir);
|
|
765
2097
|
let angle = Math.atan2(this.camDir.z, this.camDir.x) + Math.PI / 2;
|
|
766
2098
|
angle = 2 * Math.PI - angle;
|
|
@@ -775,47 +2107,56 @@ var PlayerController = class {
|
|
|
775
2107
|
if (this.spacePressed) this.moveDir.add(this.DIR_UP);
|
|
776
2108
|
}
|
|
777
2109
|
if (this.isFlying && this.fwdPressed) {
|
|
778
|
-
this.
|
|
2110
|
+
this.curPlayerSpeed = this.shiftPressed ? this.playerFlySpeed * 2 : this.playerFlySpeed;
|
|
779
2111
|
} else {
|
|
780
|
-
this.
|
|
2112
|
+
this.curPlayerSpeed = this.shiftPressed ? this.playerSpeed * 2 : this.playerSpeed;
|
|
781
2113
|
}
|
|
782
2114
|
this.moveDir.normalize().applyAxisAngle(this.upVector, angle);
|
|
783
|
-
this.player.position.addScaledVector(
|
|
2115
|
+
this.player.position.addScaledVector(
|
|
2116
|
+
this.moveDir,
|
|
2117
|
+
this.curPlayerSpeed * delta
|
|
2118
|
+
);
|
|
784
2119
|
let playerDistanceFromGround = Infinity;
|
|
785
|
-
this._originTmp.set(
|
|
2120
|
+
this._originTmp.set(
|
|
2121
|
+
this.player.position.x,
|
|
2122
|
+
this.player.position.y,
|
|
2123
|
+
this.player.position.z
|
|
2124
|
+
);
|
|
786
2125
|
this._raycaster.ray.origin.copy(this._originTmp);
|
|
787
|
-
const intersects = this._raycaster.intersectObject(
|
|
2126
|
+
const intersects = this._raycaster.intersectObject(
|
|
2127
|
+
this.collider,
|
|
2128
|
+
false
|
|
2129
|
+
);
|
|
788
2130
|
if (intersects.length > 0) {
|
|
789
2131
|
playerDistanceFromGround = this.player.position.y - intersects[0].point.y;
|
|
790
|
-
const normal = intersects[0].normal;
|
|
791
|
-
const angle2 = this.getAngleWithYAxis(normal) * 180 / Math.PI;
|
|
792
2132
|
const maxH = this.playerHeight * this.playerModel.scale * 0.9;
|
|
793
2133
|
const h = this.playerHeight * this.playerModel.scale * 0.75;
|
|
794
2134
|
const minH = this.playerHeight * this.playerModel.scale * 0.7;
|
|
795
2135
|
if (!this.isFlying) {
|
|
796
|
-
if (playerDistanceFromGround
|
|
2136
|
+
if (playerDistanceFromGround >= maxH) {
|
|
797
2137
|
this.playerVelocity.y += delta * this.gravity;
|
|
798
|
-
this.player.position.addScaledVector(
|
|
2138
|
+
this.player.position.addScaledVector(
|
|
2139
|
+
this.playerVelocity,
|
|
2140
|
+
delta
|
|
2141
|
+
);
|
|
799
2142
|
this.playerIsOnGround = false;
|
|
800
|
-
} else if (playerDistanceFromGround
|
|
801
|
-
if (
|
|
802
|
-
this.playerVelocity.
|
|
803
|
-
this.player.position.addScaledVector(this.playerVelocity, delta);
|
|
2143
|
+
} else if (playerDistanceFromGround >= h && playerDistanceFromGround < maxH) {
|
|
2144
|
+
if (!this.spacePressed) {
|
|
2145
|
+
this.playerVelocity.set(0, 0, 0);
|
|
804
2146
|
this.playerIsOnGround = true;
|
|
805
|
-
|
|
806
|
-
if (this.spacePressed) {
|
|
807
|
-
this.playerVelocity.y += delta * this.gravity;
|
|
808
|
-
} else {
|
|
809
|
-
this.playerVelocity.set(0, 0, 0);
|
|
810
|
-
this.playerIsOnGround = true;
|
|
811
|
-
}
|
|
2147
|
+
this.player.position.y = intersects[0].point.y + h;
|
|
812
2148
|
}
|
|
813
|
-
} else if (playerDistanceFromGround
|
|
2149
|
+
} else if (playerDistanceFromGround >= minH && playerDistanceFromGround < h) {
|
|
814
2150
|
this.playerVelocity.set(0, 0, 0);
|
|
815
2151
|
this.playerIsOnGround = true;
|
|
2152
|
+
this.player.position.y = intersects[0].point.y + h;
|
|
816
2153
|
} else if (playerDistanceFromGround < minH) {
|
|
817
2154
|
this.playerVelocity.set(0, 0, 0);
|
|
818
|
-
this.player.position.set(
|
|
2155
|
+
this.player.position.set(
|
|
2156
|
+
this.player.position.x,
|
|
2157
|
+
intersects[0].point.y + h,
|
|
2158
|
+
this.player.position.z
|
|
2159
|
+
);
|
|
819
2160
|
this.playerIsOnGround = true;
|
|
820
2161
|
}
|
|
821
2162
|
}
|
|
@@ -830,25 +2171,57 @@ var PlayerController = class {
|
|
|
830
2171
|
this.tempBox.expandByPoint(this.tempSegment.start);
|
|
831
2172
|
this.tempBox.expandByPoint(this.tempSegment.end);
|
|
832
2173
|
this.tempBox.expandByScalar(capsuleInfo.radius);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
2174
|
+
if (!this.isMovingToBoardingPoint) {
|
|
2175
|
+
this.collider?.geometry?.boundsTree?.shapecast({
|
|
2176
|
+
intersectsBounds: (box) => box.intersectsBox(this.tempBox),
|
|
2177
|
+
intersectsTriangle: (tri) => {
|
|
2178
|
+
const triPoint = this.tempVector;
|
|
2179
|
+
const capsulePoint = this.tempVector2;
|
|
2180
|
+
const distance = tri.closestPointToSegment(
|
|
2181
|
+
this.tempSegment,
|
|
2182
|
+
triPoint,
|
|
2183
|
+
capsulePoint
|
|
2184
|
+
);
|
|
2185
|
+
if (distance < capsuleInfo.radius) {
|
|
2186
|
+
const normal = tri.getNormal(new THREE3.Vector3());
|
|
2187
|
+
if (normal.y > 0.5 && !this.isFlying) return;
|
|
2188
|
+
const depth = capsuleInfo.radius - distance;
|
|
2189
|
+
const direction = capsulePoint.sub(triPoint).normalize();
|
|
2190
|
+
this.tempSegment.start.addScaledVector(
|
|
2191
|
+
direction,
|
|
2192
|
+
depth
|
|
2193
|
+
);
|
|
2194
|
+
this.tempSegment.end.addScaledVector(direction, depth);
|
|
2195
|
+
}
|
|
847
2196
|
}
|
|
848
|
-
}
|
|
849
|
-
|
|
2197
|
+
});
|
|
2198
|
+
this.dynamicCollider?.geometry?.boundsTree?.shapecast({
|
|
2199
|
+
intersectsBounds: (box) => box.intersectsBox(this.tempBox),
|
|
2200
|
+
intersectsTriangle: (tri) => {
|
|
2201
|
+
const triPoint = this.tempVector;
|
|
2202
|
+
const capsulePoint = this.tempVector2;
|
|
2203
|
+
const distance = tri.closestPointToSegment(
|
|
2204
|
+
this.tempSegment,
|
|
2205
|
+
triPoint,
|
|
2206
|
+
capsulePoint
|
|
2207
|
+
);
|
|
2208
|
+
if (distance < capsuleInfo.radius) {
|
|
2209
|
+
const depth = capsuleInfo.radius - distance;
|
|
2210
|
+
const direction = capsulePoint.sub(triPoint).normalize();
|
|
2211
|
+
this.tempSegment.start.addScaledVector(
|
|
2212
|
+
direction,
|
|
2213
|
+
depth
|
|
2214
|
+
);
|
|
2215
|
+
this.tempSegment.end.addScaledVector(direction, depth);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
850
2220
|
const newPosition = this.tempVector.copy(this.tempSegment.start).applyMatrix4(this.collider.matrixWorld);
|
|
851
|
-
const deltaVector = this.tempVector2.subVectors(
|
|
2221
|
+
const deltaVector = this.tempVector2.subVectors(
|
|
2222
|
+
newPosition,
|
|
2223
|
+
this.player.position
|
|
2224
|
+
);
|
|
852
2225
|
const offset = Math.max(0, deltaVector.length() - 1e-5);
|
|
853
2226
|
deltaVector.normalize().multiplyScalar(offset);
|
|
854
2227
|
this.player.position.add(deltaVector);
|
|
@@ -865,102 +2238,178 @@ var PlayerController = class {
|
|
|
865
2238
|
} else {
|
|
866
2239
|
lookTarget = this.player.position.clone().add(this.camDir);
|
|
867
2240
|
}
|
|
868
|
-
this.targetMat.lookAt(
|
|
2241
|
+
this.targetMat.lookAt(
|
|
2242
|
+
this.player.position,
|
|
2243
|
+
lookTarget,
|
|
2244
|
+
this.player.up
|
|
2245
|
+
);
|
|
869
2246
|
this.targetQuat.setFromRotationMatrix(this.targetMat);
|
|
870
2247
|
const alpha = Math.min(1, this.rotationSpeed * delta);
|
|
871
2248
|
this.player.quaternion.slerp(this.targetQuat, alpha);
|
|
872
2249
|
}
|
|
873
2250
|
if ((this.thirdMouseMode === 1 || this.thirdMouseMode === 3) && this.moveDir.lengthSq() > 0) {
|
|
874
2251
|
lookTarget = this.player.position.clone().add(this.moveDir);
|
|
875
|
-
this.targetMat.lookAt(
|
|
2252
|
+
this.targetMat.lookAt(
|
|
2253
|
+
this.player.position,
|
|
2254
|
+
lookTarget,
|
|
2255
|
+
this.player.up
|
|
2256
|
+
);
|
|
876
2257
|
this.targetQuat.setFromRotationMatrix(this.targetMat);
|
|
877
2258
|
const alpha = Math.min(1, this.rotationSpeed * delta);
|
|
878
2259
|
this.player.quaternion.slerp(this.targetQuat, alpha);
|
|
879
2260
|
}
|
|
880
2261
|
}
|
|
881
2262
|
if (this.isFlying) {
|
|
882
|
-
this.
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
2263
|
+
if (!this.isFirstPerson) {
|
|
2264
|
+
this.camDir.y = 0;
|
|
2265
|
+
this.camDir.normalize();
|
|
2266
|
+
this.camDir.negate();
|
|
2267
|
+
this.moveDir.normalize();
|
|
2268
|
+
this.moveDir.negate();
|
|
2269
|
+
const lookTarget = this.player.position.clone().add(this.fwdPressed ? this.moveDir : this.camDir);
|
|
2270
|
+
this.targetMat.lookAt(
|
|
2271
|
+
this.player.position,
|
|
2272
|
+
lookTarget,
|
|
2273
|
+
this.player.up
|
|
2274
|
+
);
|
|
2275
|
+
this.targetQuat.setFromRotationMatrix(this.targetMat);
|
|
2276
|
+
const alpha = Math.min(1, this.rotationSpeed * delta);
|
|
2277
|
+
this.player.quaternion.slerp(this.targetQuat, alpha);
|
|
2278
|
+
}
|
|
892
2279
|
}
|
|
893
2280
|
if (!this.isFirstPerson) {
|
|
894
2281
|
const lookTarget = this.player.position.clone();
|
|
895
|
-
lookTarget.y +=
|
|
2282
|
+
lookTarget.y += this.playerHeight / 8 * this.playerModel.scale;
|
|
896
2283
|
this.camera.position.sub(this.controls.target);
|
|
897
2284
|
this.controls.target.copy(lookTarget);
|
|
898
2285
|
this.camera.position.add(lookTarget);
|
|
899
2286
|
this.controls.update();
|
|
900
2287
|
if (!this.enableZoom) {
|
|
901
|
-
this._personToCam.subVectors(
|
|
902
|
-
|
|
2288
|
+
this._personToCam.subVectors(
|
|
2289
|
+
this.camera.position,
|
|
2290
|
+
this.player.position
|
|
2291
|
+
);
|
|
2292
|
+
const origin = this.player.position.clone();
|
|
903
2293
|
const direction = this._personToCam.clone().normalize();
|
|
904
2294
|
const desiredDist = this._personToCam.length();
|
|
905
2295
|
this._raycasterPersonToCam.set(origin, direction);
|
|
906
2296
|
this._raycasterPersonToCam.far = desiredDist;
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
2297
|
+
const intersectsCamera = this._raycasterPersonToCam.intersectObject(
|
|
2298
|
+
this.collider,
|
|
2299
|
+
false
|
|
2300
|
+
);
|
|
2301
|
+
if (intersectsCamera.length > 0) {
|
|
2302
|
+
const hit = intersectsCamera[0];
|
|
2303
|
+
const safeDist = Math.max(
|
|
2304
|
+
hit.distance - this._camEpsilon,
|
|
2305
|
+
this.minCamDistance
|
|
2306
|
+
);
|
|
911
2307
|
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
912
|
-
this.camera.position.lerp(
|
|
2308
|
+
this.camera.position.lerp(
|
|
2309
|
+
targetCamPos,
|
|
2310
|
+
this._camCollisionLerp
|
|
2311
|
+
);
|
|
913
2312
|
} else {
|
|
914
|
-
this._raycasterPersonToCam.far = this.
|
|
915
|
-
const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
|
|
916
|
-
|
|
2313
|
+
this._raycasterPersonToCam.far = this.maxCamDistance;
|
|
2314
|
+
const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
|
|
2315
|
+
this.collider,
|
|
2316
|
+
false
|
|
2317
|
+
);
|
|
2318
|
+
let safeDist = this.maxCamDistance;
|
|
917
2319
|
if (intersectsMaxDis.length) {
|
|
918
2320
|
const hitMax = intersectsMaxDis[0];
|
|
919
2321
|
safeDist = hitMax.distance - this._camEpsilon;
|
|
920
2322
|
}
|
|
921
2323
|
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
922
|
-
this.camera.position.lerp(
|
|
2324
|
+
this.camera.position.lerp(
|
|
2325
|
+
targetCamPos,
|
|
2326
|
+
this._camCollisionLerp
|
|
2327
|
+
);
|
|
923
2328
|
}
|
|
924
2329
|
}
|
|
925
2330
|
}
|
|
926
2331
|
if (this.player.position.y < this.boundingBoxMinY - 1) {
|
|
927
|
-
this._originTmp.set(
|
|
2332
|
+
this._originTmp.set(
|
|
2333
|
+
this.player.position.x,
|
|
2334
|
+
1e4,
|
|
2335
|
+
this.player.position.z
|
|
2336
|
+
);
|
|
928
2337
|
this._raycaster.ray.origin.copy(this._originTmp);
|
|
929
|
-
const
|
|
930
|
-
|
|
2338
|
+
const intersectsFall = this._raycaster.intersectObject(
|
|
2339
|
+
this.collider,
|
|
2340
|
+
false
|
|
2341
|
+
);
|
|
2342
|
+
if (intersectsFall.length > 0) {
|
|
931
2343
|
console.log("\u73A9\u5BB6\u4E3Abug\u610F\u5916\u6389\u843D");
|
|
932
|
-
this.reset(
|
|
2344
|
+
this.reset(
|
|
2345
|
+
new THREE3.Vector3(
|
|
2346
|
+
this.player.position.x,
|
|
2347
|
+
intersectsFall[0].point.y + 5,
|
|
2348
|
+
this.player.position.z
|
|
2349
|
+
)
|
|
2350
|
+
);
|
|
933
2351
|
} else {
|
|
934
2352
|
console.log("\u73A9\u5BB6\u6B63\u5E38\u6389\u843D");
|
|
935
|
-
this.reset(
|
|
2353
|
+
this.reset(
|
|
2354
|
+
new THREE3.Vector3(
|
|
2355
|
+
this.player.position.x,
|
|
2356
|
+
this.player.position.y + 15,
|
|
2357
|
+
this.player.position.z
|
|
2358
|
+
)
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (this.isShowMobileControls) {
|
|
2363
|
+
if (this.vehicles.length) {
|
|
2364
|
+
let near = false;
|
|
2365
|
+
for (const v of this.vehicles) {
|
|
2366
|
+
this.nearCheckLocal.copy(v.boardingPoint).multiplyScalar(v.scale);
|
|
2367
|
+
v.vehicleGroup.localToWorld(
|
|
2368
|
+
this.nearCheckWorld.copy(this.nearCheckLocal)
|
|
2369
|
+
);
|
|
2370
|
+
if (this.player.position.distanceTo(this.nearCheckWorld) < 800 * this.playerModel.scale) {
|
|
2371
|
+
near = true;
|
|
2372
|
+
this.syncVehicleBtnEl(near);
|
|
2373
|
+
break;
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
if (near !== this.isNearVehicle) {
|
|
2377
|
+
this.isNearVehicle = near;
|
|
2378
|
+
this.syncVehicleBtnEl(near);
|
|
2379
|
+
}
|
|
2380
|
+
} else {
|
|
2381
|
+
this.isNearVehicle = false;
|
|
2382
|
+
this.syncVehicleBtnEl(false);
|
|
936
2383
|
}
|
|
937
2384
|
}
|
|
938
2385
|
}
|
|
2386
|
+
/**
|
|
2387
|
+
* 获取屏幕中心点向前射线与碰撞体的交点
|
|
2388
|
+
*/
|
|
2389
|
+
getCenterScreenRaycastHit() {
|
|
2390
|
+
this.camera.updateMatrixWorld();
|
|
2391
|
+
this.centerRay.setFromCamera(this.centerMouse, this.camera);
|
|
2392
|
+
const intersects = this.centerRay.intersectObject(this.collider, false);
|
|
2393
|
+
return intersects[0];
|
|
2394
|
+
}
|
|
939
2395
|
/**
|
|
940
2396
|
* 更新模型动画
|
|
941
2397
|
*/
|
|
942
2398
|
updateMixers(delta) {
|
|
943
2399
|
if (this.personMixer) this.personMixer.update(delta);
|
|
944
|
-
|
|
2400
|
+
for (const v of this.vehicles) {
|
|
2401
|
+
v.vehicleMixer?.update(delta);
|
|
2402
|
+
}
|
|
945
2403
|
}
|
|
946
|
-
/**
|
|
947
|
-
* 重置玩家位置
|
|
948
|
-
*/
|
|
949
2404
|
reset(position) {
|
|
950
2405
|
if (!this.player) return;
|
|
951
2406
|
this.playerVelocity.set(0, 0, 0);
|
|
952
2407
|
this.player.position.copy(position ?? this.initPos);
|
|
953
2408
|
}
|
|
954
|
-
/**
|
|
955
|
-
* 获取玩家位置
|
|
956
|
-
*/
|
|
957
2409
|
getPosition() {
|
|
958
|
-
return this.player
|
|
2410
|
+
return this.player?.position;
|
|
959
2411
|
}
|
|
960
2412
|
// ==================== 输入处理 ====================
|
|
961
|
-
/**
|
|
962
|
-
* 设置输入
|
|
963
|
-
*/
|
|
964
2413
|
setInput(input) {
|
|
965
2414
|
if (typeof input.moveX === "number") {
|
|
966
2415
|
this.lftPressed = input.moveX === -1;
|
|
@@ -977,7 +2426,14 @@ var PlayerController = class {
|
|
|
977
2426
|
}
|
|
978
2427
|
if (typeof input.jump === "boolean") {
|
|
979
2428
|
if (input.jump) {
|
|
2429
|
+
if (this.isMovingToBoardingPoint) {
|
|
2430
|
+
this.isMovingToBoardingPoint = false;
|
|
2431
|
+
this.boardingWaypoints = [];
|
|
2432
|
+
this.currentWaypointIndex = 0;
|
|
2433
|
+
this.boardingTargetDir = null;
|
|
2434
|
+
}
|
|
980
2435
|
this.spacePressed = true;
|
|
2436
|
+
if (this.controllerMode == 1) return;
|
|
981
2437
|
if (!this.playerIsOnGround || this.isFlying) return;
|
|
982
2438
|
this.playPersonAnimationByName("jumping");
|
|
983
2439
|
this.playerVelocity.y = this.jumpHeight;
|
|
@@ -992,17 +2448,21 @@ var PlayerController = class {
|
|
|
992
2448
|
if (input.toggleView) {
|
|
993
2449
|
this.changeView();
|
|
994
2450
|
}
|
|
995
|
-
if (input.toggleFly) {
|
|
2451
|
+
if (input.toggleFly && this.playerFlyEnabled && this.controllerMode == 0) {
|
|
996
2452
|
this.isFlying = !this.isFlying;
|
|
997
2453
|
this.setAnimationByPressed();
|
|
998
2454
|
if (!this.isFlying && !this.playerIsOnGround) {
|
|
999
2455
|
this.playPersonAnimationByName("jumping");
|
|
1000
2456
|
}
|
|
1001
2457
|
}
|
|
2458
|
+
if (input.toggleVehicle) {
|
|
2459
|
+
if (this.controllerMode == 0) {
|
|
2460
|
+
this.enterVehicle();
|
|
2461
|
+
} else {
|
|
2462
|
+
this.exitVehicle();
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
1002
2465
|
}
|
|
1003
|
-
/**
|
|
1004
|
-
* 事件绑定
|
|
1005
|
-
*/
|
|
1006
2466
|
onAllEvent() {
|
|
1007
2467
|
this.isupdate = true;
|
|
1008
2468
|
this.setPointerLock();
|
|
@@ -1011,9 +2471,6 @@ var PlayerController = class {
|
|
|
1011
2471
|
window.addEventListener("mousemove", this._mouseMove);
|
|
1012
2472
|
window.addEventListener("click", this._mouseClick);
|
|
1013
2473
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* 事件解绑
|
|
1016
|
-
*/
|
|
1017
2474
|
offAllEvent() {
|
|
1018
2475
|
this.isupdate = false;
|
|
1019
2476
|
document.exitPointerLock();
|
|
@@ -1022,9 +2479,6 @@ var PlayerController = class {
|
|
|
1022
2479
|
window.removeEventListener("mousemove", this._mouseMove);
|
|
1023
2480
|
window.removeEventListener("click", this._mouseClick);
|
|
1024
2481
|
}
|
|
1025
|
-
/**
|
|
1026
|
-
* 初始化移动端摇杆控制
|
|
1027
|
-
*/
|
|
1028
2482
|
async initMobileControls() {
|
|
1029
2483
|
this.controls.maxPolarAngle = Math.PI * (300 / 360);
|
|
1030
2484
|
this.controls.touches = { ONE: null, TWO: null };
|
|
@@ -1047,11 +2501,17 @@ var PlayerController = class {
|
|
|
1047
2501
|
userSelect: "none"
|
|
1048
2502
|
});
|
|
1049
2503
|
container.appendChild(this.joystickZoneEl);
|
|
1050
|
-
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
2504
|
+
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
2505
|
+
(evtName) => {
|
|
2506
|
+
this.joystickZoneEl?.addEventListener(
|
|
2507
|
+
evtName,
|
|
2508
|
+
(e) => e.preventDefault(),
|
|
2509
|
+
{
|
|
2510
|
+
passive: false
|
|
2511
|
+
}
|
|
2512
|
+
);
|
|
2513
|
+
}
|
|
2514
|
+
);
|
|
1055
2515
|
this.joystickManager = nipple.create({
|
|
1056
2516
|
zone: this.joystickZoneEl,
|
|
1057
2517
|
mode: "static",
|
|
@@ -1074,15 +2534,22 @@ var PlayerController = class {
|
|
|
1074
2534
|
const dirY = rawY > deadzone ? 1 : rawY < -deadzone ? -1 : 0;
|
|
1075
2535
|
const sprintThreshold = JOY_SIZE / 2;
|
|
1076
2536
|
const isSprinting = distance >= sprintThreshold;
|
|
1077
|
-
const prev = this.prevJoyState || {
|
|
1078
|
-
|
|
2537
|
+
const prev = this.prevJoyState || {
|
|
2538
|
+
dirX: 0,
|
|
2539
|
+
dirY: 0,
|
|
2540
|
+
shift: false
|
|
2541
|
+
};
|
|
2542
|
+
if (dirX === prev.dirX && dirY === prev.dirY && isSprinting === prev.shift)
|
|
1079
2543
|
return;
|
|
1080
|
-
}
|
|
1081
2544
|
this.prevJoyState = { dirX, dirY, shift: isSprinting };
|
|
1082
2545
|
this.setInput({ moveX: dirX, moveY: dirY, shift: isSprinting });
|
|
1083
2546
|
});
|
|
1084
2547
|
this.joystickManager.on("end", () => {
|
|
1085
|
-
const prev = this.prevJoyState || {
|
|
2548
|
+
const prev = this.prevJoyState || {
|
|
2549
|
+
dirX: 0,
|
|
2550
|
+
dirY: 0,
|
|
2551
|
+
shift: false
|
|
2552
|
+
};
|
|
1086
2553
|
if (prev.dirX !== 0 || prev.dirY !== 0 || prev.shift !== false) {
|
|
1087
2554
|
this.prevJoyState = { dirX: 0, dirY: 0, shift: false };
|
|
1088
2555
|
this.setInput({ moveX: 0, moveY: 0, shift: false });
|
|
@@ -1101,15 +2568,29 @@ var PlayerController = class {
|
|
|
1101
2568
|
userSelect: "none"
|
|
1102
2569
|
});
|
|
1103
2570
|
container.appendChild(this.lookAreaEl);
|
|
1104
|
-
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
2571
|
+
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
2572
|
+
(evtName) => {
|
|
2573
|
+
this.lookAreaEl?.addEventListener(
|
|
2574
|
+
evtName,
|
|
2575
|
+
(e) => e.preventDefault(),
|
|
2576
|
+
{
|
|
2577
|
+
passive: false
|
|
2578
|
+
}
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
);
|
|
2582
|
+
this.lookAreaEl.addEventListener("pointerdown", this.onPointerDown, {
|
|
2583
|
+
passive: false
|
|
2584
|
+
});
|
|
2585
|
+
this.lookAreaEl.addEventListener("pointermove", this.onPointerMove, {
|
|
2586
|
+
passive: false
|
|
2587
|
+
});
|
|
2588
|
+
this.lookAreaEl.addEventListener("pointerup", this.onPointerUp, {
|
|
2589
|
+
passive: false
|
|
2590
|
+
});
|
|
2591
|
+
this.lookAreaEl.addEventListener("pointercancel", this.onPointerUp, {
|
|
2592
|
+
passive: false
|
|
1108
2593
|
});
|
|
1109
|
-
this.lookAreaEl.addEventListener("pointerdown", this.onPointerDown, { passive: false });
|
|
1110
|
-
this.lookAreaEl.addEventListener("pointermove", this.onPointerMove, { passive: false });
|
|
1111
|
-
this.lookAreaEl.addEventListener("pointerup", this.onPointerUp, { passive: false });
|
|
1112
|
-
this.lookAreaEl.addEventListener("pointercancel", this.onPointerUp, { passive: false });
|
|
1113
2594
|
const createBtn = (rightPx, bottomPx, bgUrl) => {
|
|
1114
2595
|
const btn = document.createElement("button");
|
|
1115
2596
|
const styles = {
|
|
@@ -1141,7 +2622,9 @@ var PlayerController = class {
|
|
|
1141
2622
|
Object.assign(btn.style, styles);
|
|
1142
2623
|
container.appendChild(btn);
|
|
1143
2624
|
["touchstart", "touchend", "touchcancel"].forEach((evtName) => {
|
|
1144
|
-
btn.addEventListener(evtName, (e) => e.preventDefault(), {
|
|
2625
|
+
btn.addEventListener(evtName, (e) => e.preventDefault(), {
|
|
2626
|
+
passive: false
|
|
2627
|
+
});
|
|
1145
2628
|
});
|
|
1146
2629
|
return btn;
|
|
1147
2630
|
};
|
|
@@ -1188,10 +2671,16 @@ var PlayerController = class {
|
|
|
1188
2671
|
},
|
|
1189
2672
|
{ passive: false }
|
|
1190
2673
|
);
|
|
2674
|
+
this.vehicleBtnEl = createBtn(14 + 100, 14 + 120, vehicle_default);
|
|
2675
|
+
this.vehicleBtnEl.addEventListener(
|
|
2676
|
+
"touchstart",
|
|
2677
|
+
(e) => {
|
|
2678
|
+
e.preventDefault();
|
|
2679
|
+
this.setInput({ toggleVehicle: true });
|
|
2680
|
+
},
|
|
2681
|
+
{ passive: false }
|
|
2682
|
+
);
|
|
1191
2683
|
}
|
|
1192
|
-
/**
|
|
1193
|
-
* 销毁移动端摇杆控制
|
|
1194
|
-
*/
|
|
1195
2684
|
destroyMobileControls() {
|
|
1196
2685
|
try {
|
|
1197
2686
|
if (this.joystickManager && this.joystickManager.destroy) {
|
|
@@ -1199,7 +2688,9 @@ var PlayerController = class {
|
|
|
1199
2688
|
this.joystickManager = null;
|
|
1200
2689
|
}
|
|
1201
2690
|
if (this.joystickZoneEl?.parentElement) {
|
|
1202
|
-
this.joystickZoneEl.parentElement.removeChild(
|
|
2691
|
+
this.joystickZoneEl.parentElement.removeChild(
|
|
2692
|
+
this.joystickZoneEl
|
|
2693
|
+
);
|
|
1203
2694
|
this.joystickZoneEl = null;
|
|
1204
2695
|
}
|
|
1205
2696
|
if (this.lookAreaEl?.parentElement) {
|
|
@@ -1218,18 +2709,87 @@ var PlayerController = class {
|
|
|
1218
2709
|
this.viewBtnEl.parentElement.removeChild(this.viewBtnEl);
|
|
1219
2710
|
this.viewBtnEl = null;
|
|
1220
2711
|
}
|
|
1221
|
-
this.
|
|
1222
|
-
|
|
2712
|
+
if (this.vehicleBtnEl?.parentElement) {
|
|
2713
|
+
this.vehicleBtnEl.parentElement.removeChild(this.vehicleBtnEl);
|
|
2714
|
+
this.vehicleBtnEl = null;
|
|
2715
|
+
}
|
|
2716
|
+
this.lookAreaEl?.removeEventListener(
|
|
2717
|
+
"pointerdown",
|
|
2718
|
+
this.onPointerDown
|
|
2719
|
+
);
|
|
2720
|
+
this.lookAreaEl?.removeEventListener(
|
|
2721
|
+
"pointermove",
|
|
2722
|
+
this.onPointerMove
|
|
2723
|
+
);
|
|
1223
2724
|
this.lookAreaEl?.removeEventListener("pointerup", this.onPointerUp);
|
|
1224
|
-
this.lookAreaEl?.removeEventListener(
|
|
2725
|
+
this.lookAreaEl?.removeEventListener(
|
|
2726
|
+
"pointercancel",
|
|
2727
|
+
this.onPointerUp
|
|
2728
|
+
);
|
|
1225
2729
|
} catch (e) {
|
|
1226
2730
|
console.warn("\u9500\u6BC1\u79FB\u52A8\u7AEF\u6447\u6746\u63A7\u5236\u65F6\u51FA\u9519\uFF1A", e);
|
|
1227
2731
|
}
|
|
1228
2732
|
}
|
|
2733
|
+
syncVehicleBtnEl(show) {
|
|
2734
|
+
if (!this.vehicleBtnEl) return;
|
|
2735
|
+
this.vehicleBtnEl.style.display = show ? "block" : "none";
|
|
2736
|
+
}
|
|
2737
|
+
syncControllerModeBtnEl() {
|
|
2738
|
+
if (!this.isShowMobileControls) return;
|
|
2739
|
+
if (this.controllerMode == 0) {
|
|
2740
|
+
this.flyBtnEl.style.display = "block";
|
|
2741
|
+
const overlayColor = "rgba(0,0,0,0.5)";
|
|
2742
|
+
this.jumpBtnEl.style.backgroundImage = `linear-gradient(${overlayColor}, ${overlayColor}), url("${jump_default}")`;
|
|
2743
|
+
} else {
|
|
2744
|
+
this.flyBtnEl.style.display = "none";
|
|
2745
|
+
const overlayColor = "rgba(0,0,0,0.5)";
|
|
2746
|
+
this.jumpBtnEl.style.backgroundImage = `linear-gradient(${overlayColor}, ${overlayColor}), url("${break_default}")`;
|
|
2747
|
+
this.jumpBtnEl.style.backgroundImage = `url(${break_default})`;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
// ==================== 更新参数 ====================
|
|
2751
|
+
setMouseSensitivity(mouseSensity) {
|
|
2752
|
+
this.mouseSensity = mouseSensity;
|
|
2753
|
+
this.controls.rotateSpeed = this.mouseSensity * 0.05;
|
|
2754
|
+
}
|
|
2755
|
+
setGravity(gravity) {
|
|
2756
|
+
this.gravity = gravity * this.playerModel.scale;
|
|
2757
|
+
}
|
|
2758
|
+
setJumpHeight(jumpHeight) {
|
|
2759
|
+
this.jumpHeight = jumpHeight * this.playerModel.scale;
|
|
2760
|
+
}
|
|
2761
|
+
setPlayerSpeed(playerSpeed) {
|
|
2762
|
+
this.playerSpeed = playerSpeed * this.playerModel.scale;
|
|
2763
|
+
this.curPlayerSpeed = this.playerSpeed;
|
|
2764
|
+
}
|
|
2765
|
+
setPlayerFlySpeed(playerFlySpeed) {
|
|
2766
|
+
this.playerFlySpeed = playerFlySpeed * this.playerModel.scale;
|
|
2767
|
+
}
|
|
2768
|
+
setMinCamDistance(minCamDistance) {
|
|
2769
|
+
this.minCamDistance = minCamDistance * this.playerModel.scale;
|
|
2770
|
+
}
|
|
2771
|
+
setMaxCamDistance(maxCamDistance) {
|
|
2772
|
+
this.maxCamDistance = maxCamDistance * this.playerModel.scale;
|
|
2773
|
+
this.orginMaxCamDistance = this.maxCamDistance;
|
|
2774
|
+
}
|
|
2775
|
+
setThirdMouseMode(thirdMouseMode) {
|
|
2776
|
+
this.thirdMouseMode = thirdMouseMode;
|
|
2777
|
+
this.setPointerLock();
|
|
2778
|
+
}
|
|
2779
|
+
setEnableZoom(enableZoom) {
|
|
2780
|
+
this.enableZoom = enableZoom;
|
|
2781
|
+
this.controls.enableZoom = this.enableZoom;
|
|
2782
|
+
}
|
|
2783
|
+
setDebug(debug) {
|
|
2784
|
+
if (this.collider) this.scene.remove(this.collider);
|
|
2785
|
+
if (debug) {
|
|
2786
|
+
this.scene.add(this.collider);
|
|
2787
|
+
this.player.material.opacity = 0.5;
|
|
2788
|
+
} else {
|
|
2789
|
+
this.player.material.opacity = 0;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
1229
2792
|
// ==================== 销毁 ====================
|
|
1230
|
-
/**
|
|
1231
|
-
* 销毁人物控制器
|
|
1232
|
-
*/
|
|
1233
2793
|
destroy() {
|
|
1234
2794
|
this.offAllEvent();
|
|
1235
2795
|
if (this.player) {
|
|
@@ -1251,6 +2811,12 @@ var PlayerController = class {
|
|
|
1251
2811
|
this.collider = null;
|
|
1252
2812
|
}
|
|
1253
2813
|
this.destroyMobileControls();
|
|
2814
|
+
for (const v of this.vehicles) {
|
|
2815
|
+
this.scene.remove(v.vehicleGroup);
|
|
2816
|
+
v.pathPlanner?.dispose();
|
|
2817
|
+
}
|
|
2818
|
+
this.vehicles = [];
|
|
2819
|
+
this.activeVehicle = null;
|
|
1254
2820
|
controllerInstance = null;
|
|
1255
2821
|
}
|
|
1256
2822
|
};
|
|
@@ -1259,13 +2825,30 @@ function playerController() {
|
|
|
1259
2825
|
const c = controllerInstance;
|
|
1260
2826
|
return {
|
|
1261
2827
|
init: (opts, callback) => c.init(opts, callback),
|
|
2828
|
+
loadVehicleModel: (params) => c.loadVehicleModel(params),
|
|
1262
2829
|
changeView: () => c.changeView(),
|
|
1263
2830
|
reset: (pos) => c.reset(pos),
|
|
1264
2831
|
update: (dt) => c.update(dt),
|
|
1265
2832
|
destroy: () => c.destroy(),
|
|
1266
2833
|
setInput: (i) => c.setInput(i),
|
|
1267
|
-
|
|
1268
|
-
|
|
2834
|
+
getPosition: () => c.getPosition(),
|
|
2835
|
+
getCenterScreenRaycastHit: () => c.getCenterScreenRaycastHit(),
|
|
2836
|
+
getPerson: () => c.person,
|
|
2837
|
+
getActiveVehicle: () => c.activeVehicle,
|
|
2838
|
+
getAllVehicles: () => c.vehicles,
|
|
2839
|
+
switchPlayerModel: (model) => c.switchPlayerModel(model),
|
|
2840
|
+
setMouseSensitivity: (mouseSensity) => c.setMouseSensitivity(mouseSensity),
|
|
2841
|
+
setGravity: (gravity) => c.setGravity(gravity),
|
|
2842
|
+
setJumpHeight: (jumpHeight) => c.setJumpHeight(jumpHeight),
|
|
2843
|
+
setPlayerSpeed: (playerSpeed) => c.setPlayerSpeed(playerSpeed),
|
|
2844
|
+
setPlayerFlySpeed: (playerFlySpeed) => c.setPlayerFlySpeed(playerFlySpeed),
|
|
2845
|
+
setMinCamDistance: (minCamDistance) => c.setMinCamDistance(minCamDistance),
|
|
2846
|
+
setMaxCamDistance: (maxCamDistance) => c.setMaxCamDistance(maxCamDistance),
|
|
2847
|
+
setThirdMouseMode: (thirdMouseMode) => c.setThirdMouseMode(thirdMouseMode),
|
|
2848
|
+
setEnableZoom: (enableZoom) => c.setEnableZoom(enableZoom),
|
|
2849
|
+
setDebug: (debug) => c.setDebug(debug),
|
|
2850
|
+
setOverShoulderView: (enable) => c.setOverShoulderView(enable),
|
|
2851
|
+
setPlayerScale: (scale) => c.setPlayerScale(scale)
|
|
1269
2852
|
};
|
|
1270
2853
|
}
|
|
1271
2854
|
function onAllEvent() {
|