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 CHANGED
@@ -8,7 +8,7 @@ npm install three-player-controller
8
8
 
9
9
  # 示例
10
10
 
11
- [Player Controller](https://hh-hang.github.io/three-player-controller/)
11
+ [glbScene](https://hh-hang.github.io/three-player-controller/index.html)
12
12
 
13
13
  ### 控制
14
14
 
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
- if (this.isFirstPerson) {
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.player) {
308
- this.player.add(this.camera);
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
- const mat = child.material;
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() {