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/dist/index.mjs
CHANGED
|
@@ -5,6 +5,17 @@ import { RoundedBoxGeometry } from "three/examples/jsm/geometries/RoundedBoxGeom
|
|
|
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
|
+
|
|
9
|
+
// assets/imgs/fly.png
|
|
10
|
+
var fly_default = "./fly-MARLZTYI.png";
|
|
11
|
+
|
|
12
|
+
// assets/imgs/jump.png
|
|
13
|
+
var jump_default = "./jump-QUC4EGFV.png";
|
|
14
|
+
|
|
15
|
+
// assets/imgs/view.png
|
|
16
|
+
var view_default = "./view-WKETVFPK.png";
|
|
17
|
+
|
|
18
|
+
// src/playerController.ts
|
|
8
19
|
THREE.Mesh.prototype.raycast = acceleratedRaycast;
|
|
9
20
|
var controllerInstance = null;
|
|
10
21
|
var clock = new THREE.Clock();
|
|
@@ -20,15 +31,15 @@ var PlayerController = class {
|
|
|
20
31
|
this.displayPlayer = false;
|
|
21
32
|
this.displayCollider = false;
|
|
22
33
|
this.displayVisualizer = false;
|
|
23
|
-
//
|
|
34
|
+
// 场景对象
|
|
24
35
|
this.collider = null;
|
|
25
36
|
this.visualizer = null;
|
|
26
37
|
this.person = null;
|
|
27
|
-
//
|
|
38
|
+
// 状态开关
|
|
28
39
|
this.playerIsOnGround = false;
|
|
29
40
|
this.isupdate = true;
|
|
30
41
|
this.isFlying = false;
|
|
31
|
-
//
|
|
42
|
+
// 输入状态
|
|
32
43
|
this.fwdPressed = false;
|
|
33
44
|
this.bkdPressed = false;
|
|
34
45
|
this.lftPressed = false;
|
|
@@ -36,6 +47,19 @@ var PlayerController = class {
|
|
|
36
47
|
this.spacePressed = false;
|
|
37
48
|
this.ctPressed = false;
|
|
38
49
|
this.shiftPressed = false;
|
|
50
|
+
// 移动端输入
|
|
51
|
+
this.prevJoyState = { dirX: 0, dirY: 0, shift: false };
|
|
52
|
+
// 移动控制相关
|
|
53
|
+
this.joystickManager = null;
|
|
54
|
+
this.joystickZoneEl = null;
|
|
55
|
+
this.lookAreaEl = null;
|
|
56
|
+
this.jumpBtnEl = null;
|
|
57
|
+
this.flyBtnEl = null;
|
|
58
|
+
this.viewBtnEl = null;
|
|
59
|
+
this.lookPointerId = null;
|
|
60
|
+
this.isLookDown = false;
|
|
61
|
+
this.lastTouchX = 0;
|
|
62
|
+
this.lastTouchY = 0;
|
|
39
63
|
// 第三人称
|
|
40
64
|
this._camCollisionLerp = 0.18;
|
|
41
65
|
// 平滑系数
|
|
@@ -46,7 +70,7 @@ var PlayerController = class {
|
|
|
46
70
|
this._maxCamDistance = 4.4;
|
|
47
71
|
// 摄像机最大距离
|
|
48
72
|
this.orginMaxCamDistance = 4.4;
|
|
49
|
-
//
|
|
73
|
+
// 物理/运动
|
|
50
74
|
this.playerVelocity = new THREE.Vector3();
|
|
51
75
|
// 玩家速度向量
|
|
52
76
|
this.upVector = new THREE.Vector3(0, 1, 0);
|
|
@@ -56,6 +80,8 @@ var PlayerController = class {
|
|
|
56
80
|
this.tempBox = new THREE.Box3();
|
|
57
81
|
this.tempMat = new THREE.Matrix4();
|
|
58
82
|
this.tempSegment = new THREE.Line3();
|
|
83
|
+
// 检测动画定时
|
|
84
|
+
this.recheckAnimTimer = null;
|
|
59
85
|
// 复用向量:用于相机朝向 / 移动
|
|
60
86
|
this.camDir = new THREE.Vector3();
|
|
61
87
|
this.moveDir = new THREE.Vector3();
|
|
@@ -106,6 +132,8 @@ var PlayerController = class {
|
|
|
106
132
|
case "Space":
|
|
107
133
|
this.spacePressed = true;
|
|
108
134
|
if (!this.playerIsOnGround || this.isFlying) return;
|
|
135
|
+
const next = this.personActions?.get("jumping");
|
|
136
|
+
if (next && this.actionState === next) return;
|
|
109
137
|
this.playPersonAnimationByName("jumping");
|
|
110
138
|
this.playerVelocity.y = this.jumpHeight;
|
|
111
139
|
this.playerIsOnGround = false;
|
|
@@ -119,6 +147,8 @@ var PlayerController = class {
|
|
|
119
147
|
case "KeyF":
|
|
120
148
|
this.isFlying = !this.isFlying;
|
|
121
149
|
this.setAnimationByPressed();
|
|
150
|
+
if (!this.isFlying && !this.playerIsOnGround)
|
|
151
|
+
this.playPersonAnimationByName("jumping");
|
|
122
152
|
break;
|
|
123
153
|
}
|
|
124
154
|
};
|
|
@@ -198,49 +228,51 @@ var PlayerController = class {
|
|
|
198
228
|
this.playPersonAnimationByName("walking_backward");
|
|
199
229
|
return;
|
|
200
230
|
}
|
|
201
|
-
} else {
|
|
202
|
-
this.playPersonAnimationByName("jumping");
|
|
203
231
|
}
|
|
232
|
+
if (this.recheckAnimTimer !== null) {
|
|
233
|
+
clearTimeout(this.recheckAnimTimer);
|
|
234
|
+
}
|
|
235
|
+
this.recheckAnimTimer = setTimeout(() => {
|
|
236
|
+
this.setAnimationByPressed();
|
|
237
|
+
this.recheckAnimTimer = null;
|
|
238
|
+
}, 200);
|
|
204
239
|
};
|
|
205
240
|
// 鼠标移动事件
|
|
206
241
|
this._mouseMove = (e) => {
|
|
207
242
|
if (document.pointerLockElement !== document.body) return;
|
|
208
|
-
|
|
209
|
-
const yaw = -e.movementX * 1e-4 * this.mouseSensity;
|
|
210
|
-
const pitch = -e.movementY * 1e-4 * this.mouseSensity;
|
|
211
|
-
this.player.rotateY(yaw);
|
|
212
|
-
this.camera.rotation.x = THREE.MathUtils.clamp(
|
|
213
|
-
this.camera.rotation.x + pitch,
|
|
214
|
-
-1.3,
|
|
215
|
-
1.4
|
|
216
|
-
);
|
|
217
|
-
} else {
|
|
218
|
-
const sensitivity = 1e-4 * this.mouseSensity;
|
|
219
|
-
const deltaX = -e.movementX * sensitivity;
|
|
220
|
-
const deltaY = -e.movementY * sensitivity;
|
|
221
|
-
const target = this.player.position.clone();
|
|
222
|
-
const distance = this.camera.position.distanceTo(target);
|
|
223
|
-
const currentPosition = this.camera.position.clone().sub(target);
|
|
224
|
-
let theta = Math.atan2(currentPosition.x, currentPosition.z);
|
|
225
|
-
let phi = Math.acos(currentPosition.y / distance);
|
|
226
|
-
theta += deltaX;
|
|
227
|
-
phi += deltaY;
|
|
228
|
-
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));
|
|
229
|
-
const newX = distance * Math.sin(phi) * Math.sin(theta);
|
|
230
|
-
const newY = distance * Math.cos(phi);
|
|
231
|
-
const newZ = distance * Math.sin(phi) * Math.cos(theta);
|
|
232
|
-
this.camera.position.set(
|
|
233
|
-
target.x + newX,
|
|
234
|
-
target.y + newY,
|
|
235
|
-
target.z + newZ
|
|
236
|
-
);
|
|
237
|
-
this.camera.lookAt(target);
|
|
238
|
-
}
|
|
243
|
+
this.setToward(e.movementX, e.movementY, 1e-4);
|
|
239
244
|
};
|
|
240
245
|
this._mouseClick = (e) => {
|
|
241
246
|
if (document.pointerLockElement !== document.body)
|
|
242
247
|
document.body.requestPointerLock();
|
|
243
248
|
};
|
|
249
|
+
this.onPointerDown = (e) => {
|
|
250
|
+
if (e.pointerType !== "touch") return;
|
|
251
|
+
this.isLookDown = true;
|
|
252
|
+
this.lookPointerId = e.pointerId;
|
|
253
|
+
this.lastTouchX = e.clientX;
|
|
254
|
+
this.lastTouchY = e.clientY;
|
|
255
|
+
this.lookAreaEl?.setPointerCapture && this.lookAreaEl.setPointerCapture(e.pointerId);
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
};
|
|
258
|
+
this.onPointerMove = (e) => {
|
|
259
|
+
if (!this.isLookDown || e.pointerId !== this.lookPointerId) return;
|
|
260
|
+
const dx = e.clientX - this.lastTouchX;
|
|
261
|
+
const dy = e.clientY - this.lastTouchY;
|
|
262
|
+
this.lastTouchX = e.clientX;
|
|
263
|
+
this.lastTouchY = e.clientY;
|
|
264
|
+
this.setInput({
|
|
265
|
+
lookDeltaX: dx,
|
|
266
|
+
lookDeltaY: dy
|
|
267
|
+
});
|
|
268
|
+
e.preventDefault();
|
|
269
|
+
};
|
|
270
|
+
this.onPointerUp = (e) => {
|
|
271
|
+
if (e.pointerId !== this.lookPointerId) return;
|
|
272
|
+
this.isLookDown = false;
|
|
273
|
+
this.lookPointerId = null;
|
|
274
|
+
this.lookAreaEl?.releasePointerCapture && this.lookAreaEl.releasePointerCapture(e.pointerId);
|
|
275
|
+
};
|
|
244
276
|
this._raycaster.firstHitOnly = true;
|
|
245
277
|
this._raycasterPersonToCam.firstHitOnly = true;
|
|
246
278
|
}
|
|
@@ -264,11 +296,18 @@ var PlayerController = class {
|
|
|
264
296
|
this._minCamDistance = opts.minCamDistance ? opts.minCamDistance * s : 100 * s;
|
|
265
297
|
this._maxCamDistance = opts.maxCamDistance ? opts.maxCamDistance * s : 440 * s;
|
|
266
298
|
this.orginMaxCamDistance = this._maxCamDistance;
|
|
299
|
+
this.isShowMobileControls = opts.isShowMobileControls ?? true;
|
|
300
|
+
function isMobileDevice() {
|
|
301
|
+
return navigator.maxTouchPoints && navigator.maxTouchPoints > 0 || "ontouchstart" in window || /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
302
|
+
}
|
|
303
|
+
if (isMobileDevice() && this.isShowMobileControls) {
|
|
304
|
+
this.initMobileControls();
|
|
305
|
+
}
|
|
267
306
|
await this.createBVH(opts.colliderMeshUrl);
|
|
268
307
|
this.createPlayer();
|
|
269
308
|
await this.loadPersonGLB();
|
|
270
|
-
if (this.isFirstPerson && this.
|
|
271
|
-
this.
|
|
309
|
+
if (this.isFirstPerson && this.person) {
|
|
310
|
+
this.person.add(this.camera);
|
|
272
311
|
}
|
|
273
312
|
this.onAllEvent();
|
|
274
313
|
this.setCameraPos();
|
|
@@ -367,11 +406,7 @@ var PlayerController = class {
|
|
|
367
406
|
this.person.traverse((child) => {
|
|
368
407
|
if (child.isMesh) {
|
|
369
408
|
child.castShadow = true;
|
|
370
|
-
|
|
371
|
-
if (!mat) return;
|
|
372
|
-
const mats = Array.isArray(mat) ? mat : [mat];
|
|
373
|
-
mats.forEach((m) => {
|
|
374
|
-
});
|
|
409
|
+
child.receiveShadow = true;
|
|
375
410
|
}
|
|
376
411
|
});
|
|
377
412
|
this.player.add(this.person);
|
|
@@ -754,6 +789,7 @@ var PlayerController = class {
|
|
|
754
789
|
this.scene.remove(this.collider);
|
|
755
790
|
this.collider = null;
|
|
756
791
|
}
|
|
792
|
+
this.destroyMobileControls();
|
|
757
793
|
controllerInstance = null;
|
|
758
794
|
}
|
|
759
795
|
// 事件绑定
|
|
@@ -891,6 +927,305 @@ var PlayerController = class {
|
|
|
891
927
|
}
|
|
892
928
|
this.boundingBoxMinY = this.collider.geometry.boundingBox.min.y;
|
|
893
929
|
}
|
|
930
|
+
// 设置朝向
|
|
931
|
+
setToward(dx, dy, speed) {
|
|
932
|
+
if (this.isFirstPerson) {
|
|
933
|
+
const yaw = -dx * speed * this.mouseSensity;
|
|
934
|
+
const pitch = -dy * speed * this.mouseSensity;
|
|
935
|
+
this.player.rotateY(yaw);
|
|
936
|
+
this.camera.rotation.x = THREE.MathUtils.clamp(
|
|
937
|
+
this.camera.rotation.x + pitch,
|
|
938
|
+
-1.1,
|
|
939
|
+
1.4
|
|
940
|
+
);
|
|
941
|
+
} else {
|
|
942
|
+
const sensitivity = this.mouseSensity;
|
|
943
|
+
const deltaX = -dx * speed * sensitivity;
|
|
944
|
+
const deltaY = -dy * speed * sensitivity;
|
|
945
|
+
const target = this.player.position.clone();
|
|
946
|
+
const distance = this.camera.position.distanceTo(target);
|
|
947
|
+
const currentPosition = this.camera.position.clone().sub(target);
|
|
948
|
+
let theta = Math.atan2(currentPosition.x, currentPosition.z);
|
|
949
|
+
let phi = Math.acos(currentPosition.y / distance);
|
|
950
|
+
theta += deltaX;
|
|
951
|
+
phi += deltaY;
|
|
952
|
+
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi));
|
|
953
|
+
const newX = distance * Math.sin(phi) * Math.sin(theta);
|
|
954
|
+
const newY = distance * Math.cos(phi);
|
|
955
|
+
const newZ = distance * Math.sin(phi) * Math.cos(theta);
|
|
956
|
+
this.camera.position.set(
|
|
957
|
+
target.x + newX,
|
|
958
|
+
target.y + newY,
|
|
959
|
+
target.z + newZ
|
|
960
|
+
);
|
|
961
|
+
this.camera.lookAt(target);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
// 设置输入
|
|
965
|
+
setInput(input) {
|
|
966
|
+
if (typeof input.moveX === "number") {
|
|
967
|
+
this.lftPressed = input.moveX == -1;
|
|
968
|
+
this.rgtPressed = input.moveX == 1;
|
|
969
|
+
this.setAnimationByPressed();
|
|
970
|
+
}
|
|
971
|
+
if (typeof input.moveY === "number") {
|
|
972
|
+
this.fwdPressed = input.moveY == 1;
|
|
973
|
+
this.bkdPressed = input.moveY == -1;
|
|
974
|
+
this.setAnimationByPressed();
|
|
975
|
+
}
|
|
976
|
+
if (typeof input.lookDeltaX === "number" && typeof input.lookDeltaY === "number") {
|
|
977
|
+
this.setToward(input.lookDeltaX, input.lookDeltaY, 2e-3);
|
|
978
|
+
}
|
|
979
|
+
if (typeof input.jump === "boolean") {
|
|
980
|
+
if (input.jump) {
|
|
981
|
+
this.spacePressed = true;
|
|
982
|
+
if (!this.playerIsOnGround || this.isFlying) return;
|
|
983
|
+
this.playPersonAnimationByName("jumping");
|
|
984
|
+
this.playerVelocity.y = this.jumpHeight;
|
|
985
|
+
this.playerIsOnGround = false;
|
|
986
|
+
} else {
|
|
987
|
+
this.spacePressed = false;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (typeof input.shift === "boolean") {
|
|
991
|
+
this.shiftPressed = input.shift;
|
|
992
|
+
}
|
|
993
|
+
if (input.toggleView) {
|
|
994
|
+
this.changeView();
|
|
995
|
+
}
|
|
996
|
+
if (input.toggleFly) {
|
|
997
|
+
this.isFlying = !this.isFlying;
|
|
998
|
+
this.setAnimationByPressed();
|
|
999
|
+
if (!this.isFlying && !this.playerIsOnGround)
|
|
1000
|
+
this.playPersonAnimationByName("jumping");
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
// 初始化移动端摇杆控制
|
|
1004
|
+
async initMobileControls() {
|
|
1005
|
+
this.controls.maxPolarAngle = Math.PI * (300 / 360);
|
|
1006
|
+
this.controls.touches = { ONE: null, TWO: null };
|
|
1007
|
+
const mod = (await import("nipplejs")).default;
|
|
1008
|
+
const nipple = mod;
|
|
1009
|
+
const JOY_SIZE = 120;
|
|
1010
|
+
const container = document.body;
|
|
1011
|
+
this.joystickZoneEl = document.createElement("div");
|
|
1012
|
+
this.joystickZoneEl.id = "joy-zone";
|
|
1013
|
+
Object.assign(this.joystickZoneEl.style, {
|
|
1014
|
+
position: "absolute",
|
|
1015
|
+
left: "16px",
|
|
1016
|
+
bottom: "16px",
|
|
1017
|
+
width: `${JOY_SIZE + 40}px`,
|
|
1018
|
+
height: `${JOY_SIZE + 40}px`,
|
|
1019
|
+
touchAction: "none",
|
|
1020
|
+
zIndex: "999",
|
|
1021
|
+
pointerEvents: "auto",
|
|
1022
|
+
WebkitUserSelect: "none",
|
|
1023
|
+
userSelect: "none"
|
|
1024
|
+
});
|
|
1025
|
+
container.appendChild(this.joystickZoneEl);
|
|
1026
|
+
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
1027
|
+
(evtName) => {
|
|
1028
|
+
this.joystickZoneEl?.addEventListener(
|
|
1029
|
+
evtName,
|
|
1030
|
+
(e) => {
|
|
1031
|
+
e.preventDefault();
|
|
1032
|
+
},
|
|
1033
|
+
{ passive: false }
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
);
|
|
1037
|
+
this.joystickManager = nipple.create({
|
|
1038
|
+
zone: this.joystickZoneEl,
|
|
1039
|
+
mode: "static",
|
|
1040
|
+
position: {
|
|
1041
|
+
left: `${(JOY_SIZE + 40) / 2}px`,
|
|
1042
|
+
bottom: `${(JOY_SIZE + 40) / 2}px`
|
|
1043
|
+
},
|
|
1044
|
+
color: "#ffffff",
|
|
1045
|
+
size: JOY_SIZE,
|
|
1046
|
+
multitouch: true,
|
|
1047
|
+
maxNumberOfNipples: 1
|
|
1048
|
+
});
|
|
1049
|
+
this.joystickManager.on("move", (_evt, data) => {
|
|
1050
|
+
if (!data) return;
|
|
1051
|
+
const rawX = data.vector?.x ?? 0;
|
|
1052
|
+
const rawY = data.vector?.y ?? 0;
|
|
1053
|
+
const distance = data.distance ?? 0;
|
|
1054
|
+
const deadzone = 0.5;
|
|
1055
|
+
const dirX = rawX > deadzone ? 1 : rawX < -deadzone ? -1 : 0;
|
|
1056
|
+
const dirY = rawY > deadzone ? 1 : rawY < -deadzone ? -1 : 0;
|
|
1057
|
+
const sprintThreshold = JOY_SIZE / 2;
|
|
1058
|
+
const isSprinting = distance >= sprintThreshold;
|
|
1059
|
+
const prev = this.prevJoyState || { dirX: 0, dirY: 0, shift: false };
|
|
1060
|
+
if (dirX === prev.dirX && dirY === prev.dirY && isSprinting === prev.shift) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
this.prevJoyState = { dirX, dirY, shift: isSprinting };
|
|
1064
|
+
this.setInput({ moveX: dirX, moveY: dirY, shift: isSprinting });
|
|
1065
|
+
});
|
|
1066
|
+
this.joystickManager.on("end", () => {
|
|
1067
|
+
const prev = this.prevJoyState || { dirX: 0, dirY: 0, shift: false };
|
|
1068
|
+
if (prev.dirX !== 0 || prev.dirY !== 0 || prev.shift !== false) {
|
|
1069
|
+
this.prevJoyState = { dirX: 0, dirY: 0, shift: false };
|
|
1070
|
+
this.setInput({ moveX: 0, moveY: 0, shift: false });
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
this.lookAreaEl = document.createElement("div");
|
|
1074
|
+
Object.assign(this.lookAreaEl.style, {
|
|
1075
|
+
position: "absolute",
|
|
1076
|
+
right: "0",
|
|
1077
|
+
bottom: "0",
|
|
1078
|
+
width: "50%",
|
|
1079
|
+
height: "100%",
|
|
1080
|
+
zIndex: "998",
|
|
1081
|
+
touchAction: "none",
|
|
1082
|
+
WebkitUserSelect: "none",
|
|
1083
|
+
userSelect: "none"
|
|
1084
|
+
});
|
|
1085
|
+
container.appendChild(this.lookAreaEl);
|
|
1086
|
+
["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
|
|
1087
|
+
(evtName) => {
|
|
1088
|
+
this.lookAreaEl?.addEventListener(
|
|
1089
|
+
evtName,
|
|
1090
|
+
(e) => {
|
|
1091
|
+
e.preventDefault();
|
|
1092
|
+
},
|
|
1093
|
+
{ passive: false }
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
1097
|
+
this.lookAreaEl.addEventListener("pointerdown", this.onPointerDown, {
|
|
1098
|
+
passive: false
|
|
1099
|
+
});
|
|
1100
|
+
this.lookAreaEl.addEventListener("pointermove", this.onPointerMove, {
|
|
1101
|
+
passive: false
|
|
1102
|
+
});
|
|
1103
|
+
this.lookAreaEl.addEventListener("pointerup", this.onPointerUp, {
|
|
1104
|
+
passive: false
|
|
1105
|
+
});
|
|
1106
|
+
this.lookAreaEl.addEventListener("pointercancel", this.onPointerUp, {
|
|
1107
|
+
passive: false
|
|
1108
|
+
});
|
|
1109
|
+
const createBtn = (rightPx, bottomPx, bgUrl) => {
|
|
1110
|
+
const btn = document.createElement("button");
|
|
1111
|
+
const styles = {
|
|
1112
|
+
position: "absolute",
|
|
1113
|
+
right: `${rightPx}px`,
|
|
1114
|
+
bottom: `${bottomPx}px`,
|
|
1115
|
+
width: "56px",
|
|
1116
|
+
height: "56px",
|
|
1117
|
+
zIndex: "1000",
|
|
1118
|
+
borderRadius: "50%",
|
|
1119
|
+
border: "2px solid black",
|
|
1120
|
+
background: "rgba(0,0,0)",
|
|
1121
|
+
padding: "20px",
|
|
1122
|
+
opacity: "0.95",
|
|
1123
|
+
touchAction: "none",
|
|
1124
|
+
fontSize: "14px",
|
|
1125
|
+
userSelect: "none",
|
|
1126
|
+
overflow: "hidden",
|
|
1127
|
+
boxSizing: "border-box",
|
|
1128
|
+
backgroundColor: "transparent",
|
|
1129
|
+
backgroundRepeat: "no-repeat, no-repeat",
|
|
1130
|
+
backgroundPosition: "center center, center center",
|
|
1131
|
+
backgroundSize: "100% 100%, 80% 80%"
|
|
1132
|
+
};
|
|
1133
|
+
if (bgUrl) {
|
|
1134
|
+
const overlayColor = "rgba(0,0,0,0.5)";
|
|
1135
|
+
styles.backgroundImage = `linear-gradient(${overlayColor}, ${overlayColor}), url("${bgUrl}")`;
|
|
1136
|
+
}
|
|
1137
|
+
Object.assign(btn.style, styles);
|
|
1138
|
+
container.appendChild(btn);
|
|
1139
|
+
["touchstart", "touchend", "touchcancel"].forEach((evtName) => {
|
|
1140
|
+
btn.addEventListener(
|
|
1141
|
+
evtName,
|
|
1142
|
+
(e) => {
|
|
1143
|
+
e.preventDefault();
|
|
1144
|
+
},
|
|
1145
|
+
{ passive: false }
|
|
1146
|
+
);
|
|
1147
|
+
});
|
|
1148
|
+
return btn;
|
|
1149
|
+
};
|
|
1150
|
+
this.jumpBtnEl = createBtn(14, 14, jump_default);
|
|
1151
|
+
this.jumpBtnEl.addEventListener(
|
|
1152
|
+
"touchstart",
|
|
1153
|
+
(e) => {
|
|
1154
|
+
e.preventDefault();
|
|
1155
|
+
this.setInput({ jump: true });
|
|
1156
|
+
},
|
|
1157
|
+
{ passive: false }
|
|
1158
|
+
);
|
|
1159
|
+
this.jumpBtnEl.addEventListener(
|
|
1160
|
+
"touchend",
|
|
1161
|
+
(e) => {
|
|
1162
|
+
e.preventDefault();
|
|
1163
|
+
this.setInput({ jump: false });
|
|
1164
|
+
},
|
|
1165
|
+
{ passive: false }
|
|
1166
|
+
);
|
|
1167
|
+
this.jumpBtnEl.addEventListener(
|
|
1168
|
+
"touchcancel",
|
|
1169
|
+
(e) => {
|
|
1170
|
+
e.preventDefault();
|
|
1171
|
+
this.setInput({ jump: false });
|
|
1172
|
+
},
|
|
1173
|
+
{ passive: false }
|
|
1174
|
+
);
|
|
1175
|
+
this.flyBtnEl = createBtn(14, 14 + 80, fly_default);
|
|
1176
|
+
this.flyBtnEl.addEventListener(
|
|
1177
|
+
"touchstart",
|
|
1178
|
+
(e) => {
|
|
1179
|
+
e.preventDefault();
|
|
1180
|
+
this.setInput({ toggleFly: true });
|
|
1181
|
+
},
|
|
1182
|
+
{ passive: false }
|
|
1183
|
+
);
|
|
1184
|
+
this.viewBtnEl = createBtn(14, 14 + 200, view_default);
|
|
1185
|
+
this.viewBtnEl.addEventListener(
|
|
1186
|
+
"touchstart",
|
|
1187
|
+
(e) => {
|
|
1188
|
+
e.preventDefault();
|
|
1189
|
+
this.setInput({ toggleView: true });
|
|
1190
|
+
},
|
|
1191
|
+
{ passive: false }
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
// 销毁移动端摇杆控制
|
|
1195
|
+
destroyMobileControls() {
|
|
1196
|
+
try {
|
|
1197
|
+
if (this.joystickManager && this.joystickManager.destroy) {
|
|
1198
|
+
this.joystickManager.destroy();
|
|
1199
|
+
this.joystickManager = null;
|
|
1200
|
+
}
|
|
1201
|
+
if (this.joystickZoneEl?.parentElement) {
|
|
1202
|
+
this.joystickZoneEl.parentElement.removeChild(this.joystickZoneEl);
|
|
1203
|
+
this.joystickZoneEl = null;
|
|
1204
|
+
}
|
|
1205
|
+
if (this.lookAreaEl?.parentElement) {
|
|
1206
|
+
this.lookAreaEl.parentElement.removeChild(this.lookAreaEl);
|
|
1207
|
+
this.lookAreaEl = null;
|
|
1208
|
+
}
|
|
1209
|
+
if (this.jumpBtnEl?.parentElement) {
|
|
1210
|
+
this.jumpBtnEl.parentElement.removeChild(this.jumpBtnEl);
|
|
1211
|
+
this.jumpBtnEl = null;
|
|
1212
|
+
}
|
|
1213
|
+
if (this.flyBtnEl?.parentElement) {
|
|
1214
|
+
this.flyBtnEl.parentElement.removeChild(this.flyBtnEl);
|
|
1215
|
+
this.flyBtnEl = null;
|
|
1216
|
+
}
|
|
1217
|
+
if (this.viewBtnEl?.parentElement) {
|
|
1218
|
+
this.viewBtnEl.parentElement.removeChild(this.viewBtnEl);
|
|
1219
|
+
this.viewBtnEl = null;
|
|
1220
|
+
}
|
|
1221
|
+
this.lookAreaEl?.removeEventListener("pointerdown", this.onPointerDown);
|
|
1222
|
+
this.lookAreaEl?.removeEventListener("pointermove", this.onPointerMove);
|
|
1223
|
+
this.lookAreaEl?.removeEventListener("pointerup", this.onPointerUp);
|
|
1224
|
+
this.lookAreaEl?.removeEventListener("pointercancel", this.onPointerUp);
|
|
1225
|
+
} catch (e) {
|
|
1226
|
+
console.warn("\u9500\u6BC1\u79FB\u52A8\u7AEF\u6447\u6746\u63A7\u5236\u65F6\u51FA\u9519\uFF1A", e);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
894
1229
|
};
|
|
895
1230
|
function playerController() {
|
|
896
1231
|
if (!controllerInstance) controllerInstance = new PlayerController();
|
|
@@ -900,7 +1235,8 @@ function playerController() {
|
|
|
900
1235
|
changeView: () => c.changeView(),
|
|
901
1236
|
reset: (pos) => c.reset(pos),
|
|
902
1237
|
update: (dt) => c.update(dt),
|
|
903
|
-
destroy: () => c.destroy()
|
|
1238
|
+
destroy: () => c.destroy(),
|
|
1239
|
+
setInput: (i) => c.setInput(i)
|
|
904
1240
|
};
|
|
905
1241
|
}
|
|
906
1242
|
function onAllEvent() {
|