three-player-controller 0.1.1 → 0.1.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/dist/index.mjs CHANGED
@@ -6,12 +6,14 @@ 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
  var controllerInstance = null;
9
+ var clock = new THREE.Clock();
9
10
  var PlayerController = class {
10
11
  // 射线检测时只返回第一个碰撞
11
12
  constructor() {
12
13
  this.loader = new GLTFLoader();
14
+ this.playerRadius = 45;
15
+ this.playerHeight = 180;
13
16
  this.isFirstPerson = false;
14
- this.mouseSensity = 5;
15
17
  this.boundingBoxMinY = 0;
16
18
  // 测试参数
17
19
  this.displayPlayer = false;
@@ -23,24 +25,16 @@ var PlayerController = class {
23
25
  this.person = null;
24
26
  // 状态开关
25
27
  this.playerIsOnGround = false;
26
- this.isUpdatePlayer = true;
28
+ this.isupdate = true;
27
29
  // 输入状态
28
30
  this.fwdPressed = false;
29
- // w
30
31
  this.bkdPressed = false;
31
- // s
32
32
  this.lftPressed = false;
33
- // a
34
33
  this.rgtPressed = false;
35
- // d
36
34
  this.spacePressed = false;
37
- // 空格
38
35
  this.ctPressed = false;
39
- // ctrl
40
36
  this.shiftPressed = false;
41
- // shift
42
37
  this.sustainSpacePressed = false;
43
- // 空格键是否持续按下
44
38
  this.spaceLongPressTimer = null;
45
39
  // 第三人称
46
40
  this._camCollisionLerp = 0.18;
@@ -73,14 +67,8 @@ var PlayerController = class {
73
67
  this.DIR_RGT = new THREE.Vector3(1, 0, 0);
74
68
  this._personToCam = new THREE.Vector3();
75
69
  this._originTmp = new THREE.Vector3();
76
- this._raycaster = new THREE.Raycaster(
77
- new THREE.Vector3(),
78
- new THREE.Vector3(0, -1, 0)
79
- );
80
- this._raycasterPersonToCam = new THREE.Raycaster(
81
- new THREE.Vector3(),
82
- new THREE.Vector3()
83
- );
70
+ this._raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, -1, 0));
71
+ this._raycasterPersonToCam = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3());
84
72
  // 键盘按下事件
85
73
  this._boundOnKeydown = async (e) => {
86
74
  if (e.ctrlKey && (e.code === "KeyW" || e.code === "KeyA" || e.code === "KeyS" || e.code === "KeyD")) {
@@ -203,11 +191,7 @@ var PlayerController = class {
203
191
  const yaw = -e.movementX * 1e-4 * this.mouseSensity;
204
192
  const pitch = -e.movementY * 1e-4 * this.mouseSensity;
205
193
  this.player.rotateY(yaw);
206
- this.camera.rotation.x = THREE.MathUtils.clamp(
207
- this.camera.rotation.x + pitch,
208
- -1.3,
209
- 1.4
210
- );
194
+ this.camera.rotation.x = THREE.MathUtils.clamp(this.camera.rotation.x + pitch, -1.3, 1.4);
211
195
  } else {
212
196
  const sensitivity = 1e-4 * this.mouseSensity;
213
197
  const deltaX = -e.movementX * sensitivity;
@@ -223,17 +207,12 @@ var PlayerController = class {
223
207
  const newX = distance * Math.sin(phi) * Math.sin(theta);
224
208
  const newY = distance * Math.cos(phi);
225
209
  const newZ = distance * Math.sin(phi) * Math.cos(theta);
226
- this.camera.position.set(
227
- target.x + newX,
228
- target.y + newY,
229
- target.z + newZ
230
- );
210
+ this.camera.position.set(target.x + newX, target.y + newY, target.z + newZ);
231
211
  this.camera.lookAt(target);
232
212
  }
233
213
  };
234
214
  this._mouseClick = (e) => {
235
- if (document.pointerLockElement !== document.body)
236
- document.body.requestPointerLock();
215
+ if (document.pointerLockElement !== document.body) document.body.requestPointerLock();
237
216
  };
238
217
  this._raycaster.firstHitOnly = true;
239
218
  this._raycasterPersonToCam.firstHitOnly = true;
@@ -244,13 +223,18 @@ var PlayerController = class {
244
223
  this.camera = opts.camera;
245
224
  this.controls = opts.controls;
246
225
  this.playerModel = opts.playerModel;
247
- this.playerModel.scale = opts.playerModel.scale ? opts.playerModel.scale : 1;
248
226
  this.initPos = opts.initPos ? opts.initPos : new THREE.Vector3(0, 0, 0);
249
- this.visualizeDepth = 0 * this.playerModel.scale;
250
- this.gravity = -2400 * this.playerModel.scale;
251
- this.jumpHeight = 300 * this.playerModel.scale;
252
- this.highJumpHeight = 1e3 * this.playerModel.scale;
253
- this.playerSpeed = 400 * this.playerModel.scale;
227
+ this.mouseSensity = opts.mouseSensity ? opts.mouseSensity : 5;
228
+ const s = this.playerModel.scale;
229
+ this.visualizeDepth = 0 * s;
230
+ this.gravity = opts.playerModel.gravity ? opts.playerModel.gravity * s : -2400 * s;
231
+ this.jumpHeight = opts.playerModel.jumpHeight ? opts.playerModel.jumpHeight * s : 300 * s;
232
+ this.highJumpHeight = opts.playerModel.highJumpHeight ? opts.playerModel.highJumpHeight * s : 1e3 * s;
233
+ this.playerSpeed = opts.playerModel.speed ? opts.playerModel.speed * s : 400 * s;
234
+ this._camCollisionLerp = 0.18;
235
+ this._camEpsilon = 35 * s;
236
+ this._minCamDistance = 100 * s;
237
+ this._maxCamDistance = 440 * s;
254
238
  await this.createBVH();
255
239
  this.createPlayer();
256
240
  await this.loadPersonGLB();
@@ -267,25 +251,15 @@ var PlayerController = class {
267
251
  this.isFirstPerson = !this.isFirstPerson;
268
252
  if (this.isFirstPerson) {
269
253
  this.player.attach(this.camera);
270
- this.camera.position.set(
271
- 0,
272
- 40 * this.playerModel.scale,
273
- 30 * this.playerModel.scale
274
- );
254
+ this.camera.position.set(0, 40 * this.playerModel.scale, 30 * this.playerModel.scale);
275
255
  this.camera.rotation.set(0, Math.PI, 0);
276
256
  document.body.requestPointerLock();
277
257
  } else {
278
258
  this.scene.attach(this.camera);
279
259
  const worldPos = this.player.position.clone();
280
- const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(
281
- this.player.quaternion
282
- );
260
+ const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(this.player.quaternion);
283
261
  const angle = Math.atan2(dir.z, dir.x);
284
- const offset = new THREE.Vector3(
285
- Math.cos(angle) * 400 * this.playerModel.scale,
286
- 200 * this.playerModel.scale,
287
- Math.sin(angle) * 400 * this.playerModel.scale
288
- );
262
+ const offset = new THREE.Vector3(Math.cos(angle) * 400 * this.playerModel.scale, 200 * this.playerModel.scale, Math.sin(angle) * 400 * this.playerModel.scale);
289
263
  this.camera.position.copy(worldPos).add(offset);
290
264
  this.controls.target.copy(worldPos);
291
265
  document.body.requestPointerLock();
@@ -297,15 +271,9 @@ var PlayerController = class {
297
271
  this.camera.position.set(0, 40 * this.playerModel.scale, 30 * this.playerModel.scale);
298
272
  } else {
299
273
  const worldPos = this.player.position.clone();
300
- const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(
301
- this.player.quaternion
302
- );
274
+ const dir = new THREE.Vector3(0, 0, -1).applyQuaternion(this.player.quaternion);
303
275
  const angle = Math.atan2(dir.z, dir.x);
304
- const offset = new THREE.Vector3(
305
- Math.cos(angle) * 400 * this.playerModel.scale,
306
- 200 * this.playerModel.scale,
307
- Math.sin(angle) * 400 * this.playerModel.scale
308
- );
276
+ const offset = new THREE.Vector3(Math.cos(angle) * 400 * this.playerModel.scale, 200 * this.playerModel.scale, Math.sin(angle) * 400 * this.playerModel.scale);
309
277
  this.camera.position.copy(worldPos).add(offset);
310
278
  }
311
279
  this.camera.updateProjectionMatrix();
@@ -331,28 +299,19 @@ var PlayerController = class {
331
299
  // 初始化加载器
332
300
  async initLoader() {
333
301
  const dracoLoader = new DRACOLoader();
334
- dracoLoader.setDecoderPath(
335
- "https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/"
336
- );
302
+ dracoLoader.setDecoderPath("https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/");
337
303
  dracoLoader.setDecoderConfig({ type: "js" });
338
304
  this.loader.setDRACOLoader(dracoLoader);
339
305
  }
340
306
  // 人物与动画加载
341
307
  async loadPersonGLB() {
342
308
  try {
343
- const gltf = await this.loader.loadAsync(
344
- this.playerModel.url,
345
- (xhr) => {
346
- }
347
- );
309
+ const gltf = await this.loader.loadAsync(this.playerModel.url);
348
310
  this.person = gltf.scene;
349
- this.person.name = "\u89D2\u8272";
350
- this.person.scale.set(
351
- 0.9 * this.playerModel.scale,
352
- 0.9 * this.playerModel.scale,
353
- 0.9 * this.playerModel.scale
354
- );
355
- this.person.position.set(0, -125 * this.playerModel.scale, 0);
311
+ const sc = this.playerModel.scale;
312
+ const h = this.playerHeight * sc;
313
+ this.person.scale.set(sc, sc, sc);
314
+ this.person.position.set(0, -h * 0.75, 0);
356
315
  this.player.add(this.person);
357
316
  this.reset();
358
317
  this.personMixer = new THREE.AnimationMixer(this.person);
@@ -446,32 +405,21 @@ var PlayerController = class {
446
405
  material.transparent = true;
447
406
  material.opacity = this.displayPlayer ? 0.5 : 0;
448
407
  material.wireframe = true;
449
- this.player = new THREE.Mesh(
450
- new RoundedBoxGeometry(
451
- 75 * this.playerModel.scale,
452
- 180 * this.playerModel.scale,
453
- 75 * this.playerModel.scale,
454
- 100 * this.playerModel.scale,
455
- 75 * this.playerModel.scale
456
- ),
457
- material
458
- );
459
- this.player.geometry.translate(0, -30 * this.playerModel.scale, 0);
408
+ const r = this.playerRadius * this.playerModel.scale;
409
+ const h = this.playerHeight * this.playerModel.scale;
410
+ this.player = new THREE.Mesh(new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75), material);
411
+ this.player.geometry.translate(0, -h * 0.25, 0);
460
412
  this.player.capsuleInfo = {
461
- radius: 25 * this.playerModel.scale,
462
- segment: new THREE.Line3(
463
- new THREE.Vector3(),
464
- new THREE.Vector3(0, -1, 0)
465
- )
413
+ radius: r,
414
+ segment: new THREE.Line3(new THREE.Vector3(), new THREE.Vector3(0, -h * 0.5, 0))
466
415
  };
467
- this.player.name = "\u89D2\u8272\u80F6\u56CA\u4F53";
468
- this.player.rotateY(Math.PI / 2);
416
+ this.player.name = "capsule";
469
417
  this.scene.add(this.player);
470
418
  this.reset();
471
419
  }
472
420
  // 每帧更新
473
- async updatePlayer(delta) {
474
- if (!this.isUpdatePlayer || !this.player) return;
421
+ async update(delta = clock.getDelta()) {
422
+ if (!this.isupdate || !this.player) return;
475
423
  delta = Math.min(delta, 1 / 30);
476
424
  this.updateMixers(delta);
477
425
  if (!this.collider) return;
@@ -506,29 +454,21 @@ var PlayerController = class {
506
454
  this.playerSpeed = this.shiftPressed ? 900 * this.playerModel.scale : 400 * this.playerModel.scale;
507
455
  if (this.moveDir.lengthSq() > 1e-6) {
508
456
  this.moveDir.normalize().applyAxisAngle(this.upVector, angle);
509
- this.player.position.addScaledVector(
510
- this.moveDir,
511
- this.playerSpeed * delta
512
- );
457
+ this.player.position.addScaledVector(this.moveDir, this.playerSpeed * delta);
513
458
  }
514
459
  let playerDistanceFromGround = Infinity;
515
- this._originTmp.set(
516
- this.player.position.x,
517
- this.player.position.y,
518
- this.player.position.z
519
- );
460
+ this._originTmp.set(this.player.position.x, this.player.position.y, this.player.position.z);
520
461
  this._raycaster.ray.origin.copy(this._originTmp);
521
- const intersects = this._raycaster.intersectObject(
522
- this.collider,
523
- false
524
- );
462
+ const intersects = this._raycaster.intersectObject(this.collider, false);
525
463
  if (intersects.length > 0) {
526
464
  playerDistanceFromGround = this.player.position.y - intersects[0].point.y;
527
465
  }
528
- if (playerDistanceFromGround > 130 * this.playerModel.scale) {
466
+ const h = this.playerHeight * this.playerModel.scale * 0.75;
467
+ if (playerDistanceFromGround > h) {
529
468
  this.playerVelocity.y += delta * this.gravity;
530
469
  this.player.position.addScaledVector(this.playerVelocity, delta);
531
470
  } else {
471
+ this.playerVelocity.set(0, 0, 0);
532
472
  this.playerIsOnGround = true;
533
473
  }
534
474
  this.player.updateMatrixWorld();
@@ -549,11 +489,7 @@ var PlayerController = class {
549
489
  intersectsTriangle: (tri) => {
550
490
  const triPoint = this.tempVector;
551
491
  const capsulePoint = this.tempVector2;
552
- const distance = tri.closestPointToSegment(
553
- this.tempSegment,
554
- triPoint,
555
- capsulePoint
556
- );
492
+ const distance = tri.closestPointToSegment(this.tempSegment, triPoint, capsulePoint);
557
493
  if (distance < capsuleInfo.radius) {
558
494
  const depth = capsuleInfo.radius - distance;
559
495
  const direction = capsulePoint.sub(triPoint).normalize();
@@ -563,16 +499,12 @@ var PlayerController = class {
563
499
  }
564
500
  });
565
501
  const newPosition = this.tempVector.copy(this.tempSegment.start).applyMatrix4(this.collider.matrixWorld);
566
- const deltaVector = this.tempVector2.subVectors(
567
- newPosition,
568
- this.player.position
569
- );
502
+ const deltaVector = this.tempVector2.subVectors(newPosition, this.player.position);
570
503
  const len = deltaVector.length();
571
504
  const offset = Math.max(0, len - 1e-5);
572
505
  if (offset > 0 && len > 0) {
573
506
  const n = deltaVector.multiplyScalar(1 / len);
574
507
  this.player.position.addScaledVector(n, offset);
575
- this.playerVelocity.set(0, 0, 0);
576
508
  }
577
509
  if (!this.isFirstPerson && this.moveDir.lengthSq() > 0) {
578
510
  this.camDir.y = 0;
@@ -599,25 +531,16 @@ var PlayerController = class {
599
531
  const desiredDist = this._personToCam.length();
600
532
  this._raycasterPersonToCam.set(origin, direction);
601
533
  this._raycasterPersonToCam.far = desiredDist;
602
- const intersects2 = this._raycasterPersonToCam.intersectObject(
603
- this.collider,
604
- false
605
- );
534
+ const intersects2 = this._raycasterPersonToCam.intersectObject(this.collider, false);
606
535
  if (intersects2.length > 0) {
607
536
  const hit = intersects2[0];
608
- const safeDist = Math.max(
609
- hit.distance - this._camEpsilon,
610
- this._minCamDistance
611
- );
537
+ const safeDist = Math.max(hit.distance - this._camEpsilon, this._minCamDistance);
612
538
  const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
613
539
  this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
614
540
  } else {
615
541
  const dis = this.player.position.distanceTo(this.camera.position);
616
542
  this._raycasterPersonToCam.far = this._maxCamDistance;
617
- const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
618
- this.collider,
619
- false
620
- );
543
+ const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(this.collider, false);
621
544
  if (dis < this._maxCamDistance) {
622
545
  let safeDist = this._maxCamDistance;
623
546
  if (intersectsMaxDis.length) {
@@ -630,34 +553,15 @@ var PlayerController = class {
630
553
  }
631
554
  }
632
555
  if (this.player.position.y < this.boundingBoxMinY - 1) {
633
- this._originTmp.set(
634
- this.player.position.x,
635
- 1e4,
636
- this.player.position.z
637
- );
556
+ this._originTmp.set(this.player.position.x, 1e4, this.player.position.z);
638
557
  this._raycaster.ray.origin.copy(this._originTmp);
639
- const intersects2 = this._raycaster.intersectObject(
640
- this.collider,
641
- false
642
- );
558
+ const intersects2 = this._raycaster.intersectObject(this.collider, false);
643
559
  if (intersects2.length > 0) {
644
560
  console.log("\u73A9\u5BB6\u4E3Abug\u610F\u5916\u6389\u843D");
645
- this.reset(
646
- new THREE.Vector3(
647
- this.player.position.x,
648
- intersects2[0].point.y + 5,
649
- this.player.position.z
650
- )
651
- );
561
+ this.reset(new THREE.Vector3(this.player.position.x, intersects2[0].point.y + 5, this.player.position.z));
652
562
  } else {
653
563
  console.log("\u73A9\u5BB6\u6B63\u5E38\u6389\u843D");
654
- this.reset(
655
- new THREE.Vector3(
656
- this.player.position.x,
657
- this.player.position.y + 15,
658
- this.player.position.z
659
- )
660
- );
564
+ this.reset(new THREE.Vector3(this.player.position.x, this.player.position.y + 15, this.player.position.z));
661
565
  }
662
566
  }
663
567
  }
@@ -692,7 +596,7 @@ var PlayerController = class {
692
596
  }
693
597
  // 事件绑定
694
598
  onAllEvent() {
695
- this.isUpdatePlayer = true;
599
+ this.isupdate = true;
696
600
  document.body.requestPointerLock();
697
601
  window.addEventListener("keydown", this._boundOnKeydown);
698
602
  window.addEventListener("keyup", this._boundOnKeyup);
@@ -701,7 +605,7 @@ var PlayerController = class {
701
605
  }
702
606
  // 事件解绑
703
607
  offAllEvent() {
704
- this.isUpdatePlayer = false;
608
+ this.isupdate = false;
705
609
  document.exitPointerLock();
706
610
  window.removeEventListener("keydown", this._boundOnKeydown);
707
611
  window.removeEventListener("keyup", this._boundOnKeyup);
@@ -736,7 +640,7 @@ var PlayerController = class {
736
640
  }
737
641
  this.scene.traverse((c) => {
738
642
  const mesh = c;
739
- if (mesh?.isMesh && mesh.geometry && c.name !== "\u89D2\u8272\u80F6\u56CA\u4F53") {
643
+ if (mesh?.isMesh && mesh.geometry && c.name !== "capsule") {
740
644
  try {
741
645
  let geom = mesh.geometry.clone();
742
646
  geom.applyMatrix4(mesh.matrixWorld);
@@ -762,8 +666,7 @@ var PlayerController = class {
762
666
  attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });
763
667
  } else {
764
668
  const m = attrMap.get(name);
765
- if (m.itemSize !== itemSize || m.arrayCtor !== ctor)
766
- attrConflict.add(name);
669
+ if (m.itemSize !== itemSize || m.arrayCtor !== ctor) attrConflict.add(name);
767
670
  else m.examples++;
768
671
  }
769
672
  }
@@ -784,10 +687,7 @@ var PlayerController = class {
784
687
  const meta = attrMap.get(name);
785
688
  const len = count * meta.itemSize;
786
689
  const array = new meta.arrayCtor(len);
787
- g.setAttribute(
788
- name,
789
- new THREE.BufferAttribute(array, meta.itemSize)
790
- );
690
+ g.setAttribute(name, new THREE.BufferAttribute(array, meta.itemSize));
791
691
  }
792
692
  }
793
693
  }
@@ -810,11 +710,16 @@ var PlayerController = class {
810
710
  merged.boundsTree = new MeshBVH(merged);
811
711
  this.collider = new THREE.Mesh(
812
712
  merged,
713
+ // new THREE.MeshBasicMaterial({
714
+ // color: "red",
715
+ // opacity: 0.2,
716
+ // transparent: true,
717
+ // wireframe: false,
718
+ // })
813
719
  new THREE.MeshBasicMaterial({
814
- color: "red",
815
- opacity: 0.2,
720
+ opacity: 0.5,
816
721
  transparent: true,
817
- wireframe: false
722
+ wireframe: true
818
723
  })
819
724
  );
820
725
  if (this.displayCollider) this.scene.add(this.collider);
@@ -827,7 +732,7 @@ var PlayerController = class {
827
732
  console.log("bvh\u52A0\u8F7D\u6A21\u578B\u6210\u529F", this.collider);
828
733
  }
829
734
  };
830
- function usePlayer() {
735
+ function playerController() {
831
736
  if (!controllerInstance) controllerInstance = new PlayerController();
832
737
  const c = controllerInstance;
833
738
  return {
@@ -836,8 +741,11 @@ function usePlayer() {
836
741
  createBVH: (url = "") => c.createBVH(url),
837
742
  createPlayer: () => c.createPlayer(),
838
743
  reset: (pos) => c.reset(pos),
839
- updatePlayer: (dt) => c.updatePlayer(dt),
840
- destroy: () => c.destroy()
744
+ update: (dt) => c.update(dt),
745
+ destroy: () => c.destroy(),
746
+ displayCollider: c.displayCollider,
747
+ displayPlayer: c.displayPlayer,
748
+ displayVisualizer: c.displayVisualizer
841
749
  };
842
750
  }
843
751
  function onAllEvent() {
@@ -851,6 +759,6 @@ function offAllEvent() {
851
759
  export {
852
760
  offAllEvent,
853
761
  onAllEvent,
854
- usePlayer
762
+ playerController
855
763
  };
856
764
  //# sourceMappingURL=index.mjs.map