three-player-controller 0.2.2 → 0.2.3
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 +1 -1
- package/assets/imgs/fly.png +0 -0
- package/assets/imgs/jump.png +0 -0
- package/assets/imgs/view.png +0 -0
- package/dist/fly-MARLZTYI.png +0 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +381 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +381 -45
- package/dist/index.mjs.map +1 -1
- package/dist/jump-QUC4EGFV.png +0 -0
- package/dist/view-WKETVFPK.png +0 -0
- package/package.json +44 -42
package/README.md
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.d.mts
CHANGED
|
@@ -26,6 +26,7 @@ type PlayerControllerOptions = {
|
|
|
26
26
|
minCamDistance?: number;
|
|
27
27
|
maxCamDistance?: number;
|
|
28
28
|
colliderMeshUrl?: string;
|
|
29
|
+
isShowMobileControls?: boolean;
|
|
29
30
|
};
|
|
30
31
|
declare function playerController(): {
|
|
31
32
|
init: (opts: PlayerControllerOptions, callback?: () => void) => Promise<void>;
|
|
@@ -33,6 +34,7 @@ declare function playerController(): {
|
|
|
33
34
|
reset: (pos?: THREE.Vector3) => void;
|
|
34
35
|
update: (dt?: number) => Promise<void>;
|
|
35
36
|
destroy: () => void;
|
|
37
|
+
setInput: (i: any) => void;
|
|
36
38
|
};
|
|
37
39
|
declare function onAllEvent(): void;
|
|
38
40
|
declare function offAllEvent(): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ type PlayerControllerOptions = {
|
|
|
26
26
|
minCamDistance?: number;
|
|
27
27
|
maxCamDistance?: number;
|
|
28
28
|
colliderMeshUrl?: string;
|
|
29
|
+
isShowMobileControls?: boolean;
|
|
29
30
|
};
|
|
30
31
|
declare function playerController(): {
|
|
31
32
|
init: (opts: PlayerControllerOptions, callback?: () => void) => Promise<void>;
|
|
@@ -33,6 +34,7 @@ declare function playerController(): {
|
|
|
33
34
|
reset: (pos?: THREE.Vector3) => void;
|
|
34
35
|
update: (dt?: number) => Promise<void>;
|
|
35
36
|
destroy: () => void;
|
|
37
|
+
setInput: (i: any) => void;
|
|
36
38
|
};
|
|
37
39
|
declare function onAllEvent(): void;
|
|
38
40
|
declare function offAllEvent(): void;
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,17 @@ var import_RoundedBoxGeometry = require("three/examples/jsm/geometries/RoundedBo
|
|
|
42
42
|
var import_DRACOLoader = require("three/examples/jsm/loaders/DRACOLoader.js");
|
|
43
43
|
var import_GLTFLoader = require("three/examples/jsm/loaders/GLTFLoader.js");
|
|
44
44
|
var BufferGeometryUtils = __toESM(require("three/examples/jsm/utils/BufferGeometryUtils.js"));
|
|
45
|
+
|
|
46
|
+
// assets/imgs/fly.png
|
|
47
|
+
var fly_default = "./fly-MARLZTYI.png";
|
|
48
|
+
|
|
49
|
+
// assets/imgs/jump.png
|
|
50
|
+
var jump_default = "./jump-QUC4EGFV.png";
|
|
51
|
+
|
|
52
|
+
// assets/imgs/view.png
|
|
53
|
+
var view_default = "./view-WKETVFPK.png";
|
|
54
|
+
|
|
55
|
+
// src/playerController.ts
|
|
45
56
|
THREE.Mesh.prototype.raycast = import_three_mesh_bvh.acceleratedRaycast;
|
|
46
57
|
var controllerInstance = null;
|
|
47
58
|
var clock = new THREE.Clock();
|
|
@@ -57,15 +68,15 @@ var PlayerController = class {
|
|
|
57
68
|
this.displayPlayer = false;
|
|
58
69
|
this.displayCollider = false;
|
|
59
70
|
this.displayVisualizer = false;
|
|
60
|
-
//
|
|
71
|
+
// 场景对象
|
|
61
72
|
this.collider = null;
|
|
62
73
|
this.visualizer = null;
|
|
63
74
|
this.person = null;
|
|
64
|
-
//
|
|
75
|
+
// 状态开关
|
|
65
76
|
this.playerIsOnGround = false;
|
|
66
77
|
this.isupdate = true;
|
|
67
78
|
this.isFlying = false;
|
|
68
|
-
//
|
|
79
|
+
// 输入状态
|
|
69
80
|
this.fwdPressed = false;
|
|
70
81
|
this.bkdPressed = false;
|
|
71
82
|
this.lftPressed = false;
|
|
@@ -73,6 +84,19 @@ var PlayerController = class {
|
|
|
73
84
|
this.spacePressed = false;
|
|
74
85
|
this.ctPressed = false;
|
|
75
86
|
this.shiftPressed = false;
|
|
87
|
+
// 移动端输入
|
|
88
|
+
this.prevJoyState = { dirX: 0, dirY: 0, shift: false };
|
|
89
|
+
// 移动控制相关
|
|
90
|
+
this.joystickManager = null;
|
|
91
|
+
this.joystickZoneEl = null;
|
|
92
|
+
this.lookAreaEl = null;
|
|
93
|
+
this.jumpBtnEl = null;
|
|
94
|
+
this.flyBtnEl = null;
|
|
95
|
+
this.viewBtnEl = null;
|
|
96
|
+
this.lookPointerId = null;
|
|
97
|
+
this.isLookDown = false;
|
|
98
|
+
this.lastTouchX = 0;
|
|
99
|
+
this.lastTouchY = 0;
|
|
76
100
|
// 第三人称
|
|
77
101
|
this._camCollisionLerp = 0.18;
|
|
78
102
|
// 平滑系数
|
|
@@ -83,7 +107,7 @@ var PlayerController = class {
|
|
|
83
107
|
this._maxCamDistance = 4.4;
|
|
84
108
|
// 摄像机最大距离
|
|
85
109
|
this.orginMaxCamDistance = 4.4;
|
|
86
|
-
//
|
|
110
|
+
// 物理/运动
|
|
87
111
|
this.playerVelocity = new THREE.Vector3();
|
|
88
112
|
// 玩家速度向量
|
|
89
113
|
this.upVector = new THREE.Vector3(0, 1, 0);
|
|
@@ -93,6 +117,8 @@ var PlayerController = class {
|
|
|
93
117
|
this.tempBox = new THREE.Box3();
|
|
94
118
|
this.tempMat = new THREE.Matrix4();
|
|
95
119
|
this.tempSegment = new THREE.Line3();
|
|
120
|
+
// 检测动画定时
|
|
121
|
+
this.recheckAnimTimer = null;
|
|
96
122
|
// 复用向量:用于相机朝向 / 移动
|
|
97
123
|
this.camDir = new THREE.Vector3();
|
|
98
124
|
this.moveDir = new THREE.Vector3();
|
|
@@ -143,6 +169,8 @@ var PlayerController = class {
|
|
|
143
169
|
case "Space":
|
|
144
170
|
this.spacePressed = true;
|
|
145
171
|
if (!this.playerIsOnGround || this.isFlying) return;
|
|
172
|
+
const next = this.personActions?.get("jumping");
|
|
173
|
+
if (next && this.actionState === next) return;
|
|
146
174
|
this.playPersonAnimationByName("jumping");
|
|
147
175
|
this.playerVelocity.y = this.jumpHeight;
|
|
148
176
|
this.playerIsOnGround = false;
|
|
@@ -156,6 +184,8 @@ var PlayerController = class {
|
|
|
156
184
|
case "KeyF":
|
|
157
185
|
this.isFlying = !this.isFlying;
|
|
158
186
|
this.setAnimationByPressed();
|
|
187
|
+
if (!this.isFlying && !this.playerIsOnGround)
|
|
188
|
+
this.playPersonAnimationByName("jumping");
|
|
159
189
|
break;
|
|
160
190
|
}
|
|
161
191
|
};
|
|
@@ -235,49 +265,51 @@ var PlayerController = class {
|
|
|
235
265
|
this.playPersonAnimationByName("walking_backward");
|
|
236
266
|
return;
|
|
237
267
|
}
|
|
238
|
-
} else {
|
|
239
|
-
this.playPersonAnimationByName("jumping");
|
|
240
268
|
}
|
|
269
|
+
if (this.recheckAnimTimer !== null) {
|
|
270
|
+
clearTimeout(this.recheckAnimTimer);
|
|
271
|
+
}
|
|
272
|
+
this.recheckAnimTimer = setTimeout(() => {
|
|
273
|
+
this.setAnimationByPressed();
|
|
274
|
+
this.recheckAnimTimer = null;
|
|
275
|
+
}, 200);
|
|
241
276
|
};
|
|
242
277
|
// 鼠标移动事件
|
|
243
278
|
this._mouseMove = (e) => {
|
|
244
279
|
if (document.pointerLockElement !== document.body) return;
|
|
245
|
-
|
|
246
|
-
const yaw = -e.movementX * 1e-4 * this.mouseSensity;
|
|
247
|
-
const pitch = -e.movementY * 1e-4 * this.mouseSensity;
|
|
248
|
-
this.player.rotateY(yaw);
|
|
249
|
-
this.camera.rotation.x = THREE.MathUtils.clamp(
|
|
250
|
-
this.camera.rotation.x + pitch,
|
|
251
|
-
-1.3,
|
|
252
|
-
1.4
|
|
253
|
-
);
|
|
254
|
-
} else {
|
|
255
|
-
const sensitivity = 1e-4 * this.mouseSensity;
|
|
256
|
-
const deltaX = -e.movementX * sensitivity;
|
|
257
|
-
const deltaY = -e.movementY * sensitivity;
|
|
258
|
-
const target = this.player.position.clone();
|
|
259
|
-
const distance = this.camera.position.distanceTo(target);
|
|
260
|
-
const currentPosition = this.camera.position.clone().sub(target);
|
|
261
|
-
let theta = Math.atan2(currentPosition.x, currentPosition.z);
|
|
262
|
-
let phi = Math.acos(currentPosition.y / distance);
|
|
263
|
-
theta += deltaX;
|
|
264
|
-
phi += deltaY;
|
|
265
|
-
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));
|
|
266
|
-
const newX = distance * Math.sin(phi) * Math.sin(theta);
|
|
267
|
-
const newY = distance * Math.cos(phi);
|
|
268
|
-
const newZ = distance * Math.sin(phi) * Math.cos(theta);
|
|
269
|
-
this.camera.position.set(
|
|
270
|
-
target.x + newX,
|
|
271
|
-
target.y + newY,
|
|
272
|
-
target.z + newZ
|
|
273
|
-
);
|
|
274
|
-
this.camera.lookAt(target);
|
|
275
|
-
}
|
|
280
|
+
this.setToward(e.movementX, e.movementY, 1e-4);
|
|
276
281
|
};
|
|
277
282
|
this._mouseClick = (e) => {
|
|
278
283
|
if (document.pointerLockElement !== document.body)
|
|
279
284
|
document.body.requestPointerLock();
|
|
280
285
|
};
|
|
286
|
+
this.onPointerDown = (e) => {
|
|
287
|
+
if (e.pointerType !== "touch") return;
|
|
288
|
+
this.isLookDown = true;
|
|
289
|
+
this.lookPointerId = e.pointerId;
|
|
290
|
+
this.lastTouchX = e.clientX;
|
|
291
|
+
this.lastTouchY = e.clientY;
|
|
292
|
+
this.lookAreaEl?.setPointerCapture && this.lookAreaEl.setPointerCapture(e.pointerId);
|
|
293
|
+
e.preventDefault();
|
|
294
|
+
};
|
|
295
|
+
this.onPointerMove = (e) => {
|
|
296
|
+
if (!this.isLookDown || e.pointerId !== this.lookPointerId) return;
|
|
297
|
+
const dx = e.clientX - this.lastTouchX;
|
|
298
|
+
const dy = e.clientY - this.lastTouchY;
|
|
299
|
+
this.lastTouchX = e.clientX;
|
|
300
|
+
this.lastTouchY = e.clientY;
|
|
301
|
+
this.setInput({
|
|
302
|
+
lookDeltaX: dx,
|
|
303
|
+
lookDeltaY: dy
|
|
304
|
+
});
|
|
305
|
+
e.preventDefault();
|
|
306
|
+
};
|
|
307
|
+
this.onPointerUp = (e) => {
|
|
308
|
+
if (e.pointerId !== this.lookPointerId) return;
|
|
309
|
+
this.isLookDown = false;
|
|
310
|
+
this.lookPointerId = null;
|
|
311
|
+
this.lookAreaEl?.releasePointerCapture && this.lookAreaEl.releasePointerCapture(e.pointerId);
|
|
312
|
+
};
|
|
281
313
|
this._raycaster.firstHitOnly = true;
|
|
282
314
|
this._raycasterPersonToCam.firstHitOnly = true;
|
|
283
315
|
}
|
|
@@ -301,11 +333,18 @@ var PlayerController = class {
|
|
|
301
333
|
this._minCamDistance = opts.minCamDistance ? opts.minCamDistance * s : 100 * s;
|
|
302
334
|
this._maxCamDistance = opts.maxCamDistance ? opts.maxCamDistance * s : 440 * s;
|
|
303
335
|
this.orginMaxCamDistance = this._maxCamDistance;
|
|
336
|
+
this.isShowMobileControls = opts.isShowMobileControls ?? true;
|
|
337
|
+
function isMobileDevice() {
|
|
338
|
+
return navigator.maxTouchPoints && navigator.maxTouchPoints > 0 || "ontouchstart" in window || /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
339
|
+
}
|
|
340
|
+
if (isMobileDevice() && this.isShowMobileControls) {
|
|
341
|
+
this.initMobileControls();
|
|
342
|
+
}
|
|
304
343
|
await this.createBVH(opts.colliderMeshUrl);
|
|
305
344
|
this.createPlayer();
|
|
306
345
|
await this.loadPersonGLB();
|
|
307
|
-
if (this.isFirstPerson && this.
|
|
308
|
-
this.
|
|
346
|
+
if (this.isFirstPerson && this.person) {
|
|
347
|
+
this.person.add(this.camera);
|
|
309
348
|
}
|
|
310
349
|
this.onAllEvent();
|
|
311
350
|
this.setCameraPos();
|
|
@@ -404,11 +443,7 @@ var PlayerController = class {
|
|
|
404
443
|
this.person.traverse((child) => {
|
|
405
444
|
if (child.isMesh) {
|
|
406
445
|
child.castShadow = true;
|
|
407
|
-
|
|
408
|
-
if (!mat) return;
|
|
409
|
-
const mats = Array.isArray(mat) ? mat : [mat];
|
|
410
|
-
mats.forEach((m) => {
|
|
411
|
-
});
|
|
446
|
+
child.receiveShadow = true;
|
|
412
447
|
}
|
|
413
448
|
});
|
|
414
449
|
this.player.add(this.person);
|
|
@@ -791,6 +826,7 @@ var PlayerController = class {
|
|
|
791
826
|
this.scene.remove(this.collider);
|
|
792
827
|
this.collider = null;
|
|
793
828
|
}
|
|
829
|
+
this.destroyMobileControls();
|
|
794
830
|
controllerInstance = null;
|
|
795
831
|
}
|
|
796
832
|
// 事件绑定
|
|
@@ -928,6 +964,305 @@ var PlayerController = class {
|
|
|
928
964
|
}
|
|
929
965
|
this.boundingBoxMinY = this.collider.geometry.boundingBox.min.y;
|
|
930
966
|
}
|
|
967
|
+
// 设置朝向
|
|
968
|
+
setToward(dx, dy, speed) {
|
|
969
|
+
if (this.isFirstPerson) {
|
|
970
|
+
const yaw = -dx * speed * this.mouseSensity;
|
|
971
|
+
const pitch = -dy * speed * this.mouseSensity;
|
|
972
|
+
this.player.rotateY(yaw);
|
|
973
|
+
this.camera.rotation.x = THREE.MathUtils.clamp(
|
|
974
|
+
this.camera.rotation.x + pitch,
|
|
975
|
+
-1.1,
|
|
976
|
+
1.4
|
|
977
|
+
);
|
|
978
|
+
} else {
|
|
979
|
+
const sensitivity = this.mouseSensity;
|
|
980
|
+
const deltaX = -dx * speed * sensitivity;
|
|
981
|
+
const deltaY = -dy * speed * sensitivity;
|
|
982
|
+
const target = this.player.position.clone();
|
|
983
|
+
const distance = this.camera.position.distanceTo(target);
|
|
984
|
+
const currentPosition = this.camera.position.clone().sub(target);
|
|
985
|
+
let theta = Math.atan2(currentPosition.x, currentPosition.z);
|
|
986
|
+
let phi = Math.acos(currentPosition.y / distance);
|
|
987
|
+
theta += deltaX;
|
|
988
|
+
phi += deltaY;
|
|
989
|
+
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));
|
|
990
|
+
const newX = distance * Math.sin(phi) * Math.sin(theta);
|
|
991
|
+
const newY = distance * Math.cos(phi);
|
|
992
|
+
const newZ = distance * Math.sin(phi) * Math.cos(theta);
|
|
993
|
+
this.camera.position.set(
|
|
994
|
+
target.x + newX,
|
|
995
|
+
target.y + newY,
|
|
996
|
+
target.z + newZ
|
|
997
|
+
);
|
|
998
|
+
this.camera.lookAt(target);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
// 设置输入
|
|
1002
|
+
setInput(input) {
|
|
1003
|
+
if (typeof input.moveX === "number") {
|
|
1004
|
+
this.lftPressed = input.moveX == -1;
|
|
1005
|
+
this.rgtPressed = input.moveX == 1;
|
|
1006
|
+
this.setAnimationByPressed();
|
|
1007
|
+
}
|
|
1008
|
+
if (typeof input.moveY === "number") {
|
|
1009
|
+
this.fwdPressed = input.moveY == 1;
|
|
1010
|
+
this.bkdPressed = input.moveY == -1;
|
|
1011
|
+
this.setAnimationByPressed();
|
|
1012
|
+
}
|
|
1013
|
+
if (typeof input.lookDeltaX === "number" && typeof input.lookDeltaY === "number") {
|
|
1014
|
+
this.setToward(input.lookDeltaX, input.lookDeltaY, 2e-3);
|
|
1015
|
+
}
|
|
1016
|
+
if (typeof input.jump === "boolean") {
|
|
1017
|
+
if (input.jump) {
|
|
1018
|
+
this.spacePressed = true;
|
|
1019
|
+
if (!this.playerIsOnGround || this.isFlying) return;
|
|
1020
|
+
this.playPersonAnimationByName("jumping");
|
|
1021
|
+
this.playerVelocity.y = this.jumpHeight;
|
|
1022
|
+
this.playerIsOnGround = false;
|
|
1023
|
+
} else {
|
|
1024
|
+
this.spacePressed = false;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (typeof input.shift === "boolean") {
|
|
1028
|
+
this.shiftPressed = input.shift;
|
|
1029
|
+
}
|
|
1030
|
+
if (input.toggleView) {
|
|
1031
|
+
this.changeView();
|
|
1032
|
+
}
|
|
1033
|
+
if (input.toggleFly) {
|
|
1034
|
+
this.isFlying = !this.isFlying;
|
|
1035
|
+
this.setAnimationByPressed();
|
|
1036
|
+
if (!this.isFlying && !this.playerIsOnGround)
|
|
1037
|
+
this.playPersonAnimationByName("jumping");
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
// 初始化移动端摇杆控制
|
|
1041
|
+
async initMobileControls() {
|
|
1042
|
+
this.controls.maxPolarAngle = Math.PI * (300 / 360);
|
|
1043
|
+
this.controls.touches = { ONE: null, TWO: null };
|
|
1044
|
+
const mod = (await import("nipplejs")).default;
|
|
1045
|
+
const nipple = mod;
|
|
1046
|
+
const JOY_SIZE = 120;
|
|
1047
|
+
const container = document.body;
|
|
1048
|
+
this.joystickZoneEl = document.createElement("div");
|
|
1049
|
+
this.joystickZoneEl.id = "joy-zone";
|
|
1050
|
+
Object.assign(this.joystickZoneEl.style, {
|
|
1051
|
+
position: "absolute",
|
|
1052
|
+
left: "16px",
|
|
1053
|
+
bottom: "16px",
|
|
1054
|
+
width: `${JOY_SIZE + 40}px`,
|
|
1055
|
+
height: `${JOY_SIZE + 40}px`,
|
|
1056
|
+
touchAction: "none",
|
|
1057
|
+
zIndex: "999",
|
|
1058
|
+
pointerEvents: "auto",
|
|
1059
|
+
WebkitUserSelect: "none",
|
|
1060
|
+
userSelect: "none"
|
|
1061
|
+
});
|
|
1062
|
+
container.appendChild(this.joystickZoneEl);
|
|
1063
|
+
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
1064
|
+
(evtName) => {
|
|
1065
|
+
this.joystickZoneEl?.addEventListener(
|
|
1066
|
+
evtName,
|
|
1067
|
+
(e) => {
|
|
1068
|
+
e.preventDefault();
|
|
1069
|
+
},
|
|
1070
|
+
{ passive: false }
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
this.joystickManager = nipple.create({
|
|
1075
|
+
zone: this.joystickZoneEl,
|
|
1076
|
+
mode: "static",
|
|
1077
|
+
position: {
|
|
1078
|
+
left: `${(JOY_SIZE + 40) / 2}px`,
|
|
1079
|
+
bottom: `${(JOY_SIZE + 40) / 2}px`
|
|
1080
|
+
},
|
|
1081
|
+
color: "#ffffff",
|
|
1082
|
+
size: JOY_SIZE,
|
|
1083
|
+
multitouch: true,
|
|
1084
|
+
maxNumberOfNipples: 1
|
|
1085
|
+
});
|
|
1086
|
+
this.joystickManager.on("move", (_evt, data) => {
|
|
1087
|
+
if (!data) return;
|
|
1088
|
+
const rawX = data.vector?.x ?? 0;
|
|
1089
|
+
const rawY = data.vector?.y ?? 0;
|
|
1090
|
+
const distance = data.distance ?? 0;
|
|
1091
|
+
const deadzone = 0.5;
|
|
1092
|
+
const dirX = rawX > deadzone ? 1 : rawX < -deadzone ? -1 : 0;
|
|
1093
|
+
const dirY = rawY > deadzone ? 1 : rawY < -deadzone ? -1 : 0;
|
|
1094
|
+
const sprintThreshold = JOY_SIZE / 2;
|
|
1095
|
+
const isSprinting = distance >= sprintThreshold;
|
|
1096
|
+
const prev = this.prevJoyState || { dirX: 0, dirY: 0, shift: false };
|
|
1097
|
+
if (dirX === prev.dirX && dirY === prev.dirY && isSprinting === prev.shift) {
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
this.prevJoyState = { dirX, dirY, shift: isSprinting };
|
|
1101
|
+
this.setInput({ moveX: dirX, moveY: dirY, shift: isSprinting });
|
|
1102
|
+
});
|
|
1103
|
+
this.joystickManager.on("end", () => {
|
|
1104
|
+
const prev = this.prevJoyState || { dirX: 0, dirY: 0, shift: false };
|
|
1105
|
+
if (prev.dirX !== 0 || prev.dirY !== 0 || prev.shift !== false) {
|
|
1106
|
+
this.prevJoyState = { dirX: 0, dirY: 0, shift: false };
|
|
1107
|
+
this.setInput({ moveX: 0, moveY: 0, shift: false });
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
this.lookAreaEl = document.createElement("div");
|
|
1111
|
+
Object.assign(this.lookAreaEl.style, {
|
|
1112
|
+
position: "absolute",
|
|
1113
|
+
right: "0",
|
|
1114
|
+
bottom: "0",
|
|
1115
|
+
width: "50%",
|
|
1116
|
+
height: "100%",
|
|
1117
|
+
zIndex: "998",
|
|
1118
|
+
touchAction: "none",
|
|
1119
|
+
WebkitUserSelect: "none",
|
|
1120
|
+
userSelect: "none"
|
|
1121
|
+
});
|
|
1122
|
+
container.appendChild(this.lookAreaEl);
|
|
1123
|
+
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
1124
|
+
(evtName) => {
|
|
1125
|
+
this.lookAreaEl?.addEventListener(
|
|
1126
|
+
evtName,
|
|
1127
|
+
(e) => {
|
|
1128
|
+
e.preventDefault();
|
|
1129
|
+
},
|
|
1130
|
+
{ passive: false }
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
);
|
|
1134
|
+
this.lookAreaEl.addEventListener("pointerdown", this.onPointerDown, {
|
|
1135
|
+
passive: false
|
|
1136
|
+
});
|
|
1137
|
+
this.lookAreaEl.addEventListener("pointermove", this.onPointerMove, {
|
|
1138
|
+
passive: false
|
|
1139
|
+
});
|
|
1140
|
+
this.lookAreaEl.addEventListener("pointerup", this.onPointerUp, {
|
|
1141
|
+
passive: false
|
|
1142
|
+
});
|
|
1143
|
+
this.lookAreaEl.addEventListener("pointercancel", this.onPointerUp, {
|
|
1144
|
+
passive: false
|
|
1145
|
+
});
|
|
1146
|
+
const createBtn = (rightPx, bottomPx, bgUrl) => {
|
|
1147
|
+
const btn = document.createElement("button");
|
|
1148
|
+
const styles = {
|
|
1149
|
+
position: "absolute",
|
|
1150
|
+
right: `${rightPx}px`,
|
|
1151
|
+
bottom: `${bottomPx}px`,
|
|
1152
|
+
width: "56px",
|
|
1153
|
+
height: "56px",
|
|
1154
|
+
zIndex: "1000",
|
|
1155
|
+
borderRadius: "50%",
|
|
1156
|
+
border: "2px solid black",
|
|
1157
|
+
background: "rgba(0,0,0)",
|
|
1158
|
+
padding: "20px",
|
|
1159
|
+
opacity: "0.95",
|
|
1160
|
+
touchAction: "none",
|
|
1161
|
+
fontSize: "14px",
|
|
1162
|
+
userSelect: "none",
|
|
1163
|
+
overflow: "hidden",
|
|
1164
|
+
boxSizing: "border-box",
|
|
1165
|
+
backgroundColor: "transparent",
|
|
1166
|
+
backgroundRepeat: "no-repeat, no-repeat",
|
|
1167
|
+
backgroundPosition: "center center, center center",
|
|
1168
|
+
backgroundSize: "100% 100%, 80% 80%"
|
|
1169
|
+
};
|
|
1170
|
+
if (bgUrl) {
|
|
1171
|
+
const overlayColor = "rgba(0,0,0,0.5)";
|
|
1172
|
+
styles.backgroundImage = `linear-gradient(${overlayColor}, ${overlayColor}), url("${bgUrl}")`;
|
|
1173
|
+
}
|
|
1174
|
+
Object.assign(btn.style, styles);
|
|
1175
|
+
container.appendChild(btn);
|
|
1176
|
+
["touchstart", "touchend", "touchcancel"].forEach((evtName) => {
|
|
1177
|
+
btn.addEventListener(
|
|
1178
|
+
evtName,
|
|
1179
|
+
(e) => {
|
|
1180
|
+
e.preventDefault();
|
|
1181
|
+
},
|
|
1182
|
+
{ passive: false }
|
|
1183
|
+
);
|
|
1184
|
+
});
|
|
1185
|
+
return btn;
|
|
1186
|
+
};
|
|
1187
|
+
this.jumpBtnEl = createBtn(14, 14, jump_default);
|
|
1188
|
+
this.jumpBtnEl.addEventListener(
|
|
1189
|
+
"touchstart",
|
|
1190
|
+
(e) => {
|
|
1191
|
+
e.preventDefault();
|
|
1192
|
+
this.setInput({ jump: true });
|
|
1193
|
+
},
|
|
1194
|
+
{ passive: false }
|
|
1195
|
+
);
|
|
1196
|
+
this.jumpBtnEl.addEventListener(
|
|
1197
|
+
"touchend",
|
|
1198
|
+
(e) => {
|
|
1199
|
+
e.preventDefault();
|
|
1200
|
+
this.setInput({ jump: false });
|
|
1201
|
+
},
|
|
1202
|
+
{ passive: false }
|
|
1203
|
+
);
|
|
1204
|
+
this.jumpBtnEl.addEventListener(
|
|
1205
|
+
"touchcancel",
|
|
1206
|
+
(e) => {
|
|
1207
|
+
e.preventDefault();
|
|
1208
|
+
this.setInput({ jump: false });
|
|
1209
|
+
},
|
|
1210
|
+
{ passive: false }
|
|
1211
|
+
);
|
|
1212
|
+
this.flyBtnEl = createBtn(14, 14 + 80, fly_default);
|
|
1213
|
+
this.flyBtnEl.addEventListener(
|
|
1214
|
+
"touchstart",
|
|
1215
|
+
(e) => {
|
|
1216
|
+
e.preventDefault();
|
|
1217
|
+
this.setInput({ toggleFly: true });
|
|
1218
|
+
},
|
|
1219
|
+
{ passive: false }
|
|
1220
|
+
);
|
|
1221
|
+
this.viewBtnEl = createBtn(14, 14 + 200, view_default);
|
|
1222
|
+
this.viewBtnEl.addEventListener(
|
|
1223
|
+
"touchstart",
|
|
1224
|
+
(e) => {
|
|
1225
|
+
e.preventDefault();
|
|
1226
|
+
this.setInput({ toggleView: true });
|
|
1227
|
+
},
|
|
1228
|
+
{ passive: false }
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
// 销毁移动端摇杆控制
|
|
1232
|
+
destroyMobileControls() {
|
|
1233
|
+
try {
|
|
1234
|
+
if (this.joystickManager && this.joystickManager.destroy) {
|
|
1235
|
+
this.joystickManager.destroy();
|
|
1236
|
+
this.joystickManager = null;
|
|
1237
|
+
}
|
|
1238
|
+
if (this.joystickZoneEl?.parentElement) {
|
|
1239
|
+
this.joystickZoneEl.parentElement.removeChild(this.joystickZoneEl);
|
|
1240
|
+
this.joystickZoneEl = null;
|
|
1241
|
+
}
|
|
1242
|
+
if (this.lookAreaEl?.parentElement) {
|
|
1243
|
+
this.lookAreaEl.parentElement.removeChild(this.lookAreaEl);
|
|
1244
|
+
this.lookAreaEl = null;
|
|
1245
|
+
}
|
|
1246
|
+
if (this.jumpBtnEl?.parentElement) {
|
|
1247
|
+
this.jumpBtnEl.parentElement.removeChild(this.jumpBtnEl);
|
|
1248
|
+
this.jumpBtnEl = null;
|
|
1249
|
+
}
|
|
1250
|
+
if (this.flyBtnEl?.parentElement) {
|
|
1251
|
+
this.flyBtnEl.parentElement.removeChild(this.flyBtnEl);
|
|
1252
|
+
this.flyBtnEl = null;
|
|
1253
|
+
}
|
|
1254
|
+
if (this.viewBtnEl?.parentElement) {
|
|
1255
|
+
this.viewBtnEl.parentElement.removeChild(this.viewBtnEl);
|
|
1256
|
+
this.viewBtnEl = null;
|
|
1257
|
+
}
|
|
1258
|
+
this.lookAreaEl?.removeEventListener("pointerdown", this.onPointerDown);
|
|
1259
|
+
this.lookAreaEl?.removeEventListener("pointermove", this.onPointerMove);
|
|
1260
|
+
this.lookAreaEl?.removeEventListener("pointerup", this.onPointerUp);
|
|
1261
|
+
this.lookAreaEl?.removeEventListener("pointercancel", this.onPointerUp);
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
console.warn("\u9500\u6BC1\u79FB\u52A8\u7AEF\u6447\u6746\u63A7\u5236\u65F6\u51FA\u9519\uFF1A", e);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
931
1266
|
};
|
|
932
1267
|
function playerController() {
|
|
933
1268
|
if (!controllerInstance) controllerInstance = new PlayerController();
|
|
@@ -937,7 +1272,8 @@ function playerController() {
|
|
|
937
1272
|
changeView: () => c.changeView(),
|
|
938
1273
|
reset: (pos) => c.reset(pos),
|
|
939
1274
|
update: (dt) => c.update(dt),
|
|
940
|
-
destroy: () => c.destroy()
|
|
1275
|
+
destroy: () => c.destroy(),
|
|
1276
|
+
setInput: (i) => c.setInput(i)
|
|
941
1277
|
};
|
|
942
1278
|
}
|
|
943
1279
|
function onAllEvent() {
|