three-player-controller 0.1.0 → 0.1.2

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,23 @@ 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);
470
+ console.log("delta * this.gravity", delta * this.gravity);
531
471
  } else {
472
+ console.log("\u5728\u5730\u9762");
473
+ this.playerVelocity.set(0, 0, 0);
532
474
  this.playerIsOnGround = true;
533
475
  }
534
476
  this.player.updateMatrixWorld();
@@ -549,11 +491,7 @@ var PlayerController = class {
549
491
  intersectsTriangle: (tri) => {
550
492
  const triPoint = this.tempVector;
551
493
  const capsulePoint = this.tempVector2;
552
- const distance = tri.closestPointToSegment(
553
- this.tempSegment,
554
- triPoint,
555
- capsulePoint
556
- );
494
+ const distance = tri.closestPointToSegment(this.tempSegment, triPoint, capsulePoint);
557
495
  if (distance < capsuleInfo.radius) {
558
496
  const depth = capsuleInfo.radius - distance;
559
497
  const direction = capsulePoint.sub(triPoint).normalize();
@@ -563,16 +501,12 @@ var PlayerController = class {
563
501
  }
564
502
  });
565
503
  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
- );
504
+ const deltaVector = this.tempVector2.subVectors(newPosition, this.player.position);
570
505
  const len = deltaVector.length();
571
506
  const offset = Math.max(0, len - 1e-5);
572
507
  if (offset > 0 && len > 0) {
573
508
  const n = deltaVector.multiplyScalar(1 / len);
574
509
  this.player.position.addScaledVector(n, offset);
575
- this.playerVelocity.set(0, 0, 0);
576
510
  }
577
511
  if (!this.isFirstPerson && this.moveDir.lengthSq() > 0) {
578
512
  this.camDir.y = 0;
@@ -599,25 +533,16 @@ var PlayerController = class {
599
533
  const desiredDist = this._personToCam.length();
600
534
  this._raycasterPersonToCam.set(origin, direction);
601
535
  this._raycasterPersonToCam.far = desiredDist;
602
- const intersects2 = this._raycasterPersonToCam.intersectObject(
603
- this.collider,
604
- false
605
- );
536
+ const intersects2 = this._raycasterPersonToCam.intersectObject(this.collider, false);
606
537
  if (intersects2.length > 0) {
607
538
  const hit = intersects2[0];
608
- const safeDist = Math.max(
609
- hit.distance - this._camEpsilon,
610
- this._minCamDistance
611
- );
539
+ const safeDist = Math.max(hit.distance - this._camEpsilon, this._minCamDistance);
612
540
  const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
613
541
  this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
614
542
  } else {
615
543
  const dis = this.player.position.distanceTo(this.camera.position);
616
544
  this._raycasterPersonToCam.far = this._maxCamDistance;
617
- const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
618
- this.collider,
619
- false
620
- );
545
+ const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(this.collider, false);
621
546
  if (dis < this._maxCamDistance) {
622
547
  let safeDist = this._maxCamDistance;
623
548
  if (intersectsMaxDis.length) {
@@ -630,34 +555,15 @@ var PlayerController = class {
630
555
  }
631
556
  }
632
557
  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
- );
558
+ this._originTmp.set(this.player.position.x, 1e4, this.player.position.z);
638
559
  this._raycaster.ray.origin.copy(this._originTmp);
639
- const intersects2 = this._raycaster.intersectObject(
640
- this.collider,
641
- false
642
- );
560
+ const intersects2 = this._raycaster.intersectObject(this.collider, false);
643
561
  if (intersects2.length > 0) {
644
562
  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
- );
563
+ this.reset(new THREE.Vector3(this.player.position.x, intersects2[0].point.y + 5, this.player.position.z));
652
564
  } else {
653
565
  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
- );
566
+ this.reset(new THREE.Vector3(this.player.position.x, this.player.position.y + 15, this.player.position.z));
661
567
  }
662
568
  }
663
569
  }
@@ -692,7 +598,7 @@ var PlayerController = class {
692
598
  }
693
599
  // 事件绑定
694
600
  onAllEvent() {
695
- this.isUpdatePlayer = true;
601
+ this.isupdate = true;
696
602
  document.body.requestPointerLock();
697
603
  window.addEventListener("keydown", this._boundOnKeydown);
698
604
  window.addEventListener("keyup", this._boundOnKeyup);
@@ -701,7 +607,7 @@ var PlayerController = class {
701
607
  }
702
608
  // 事件解绑
703
609
  offAllEvent() {
704
- this.isUpdatePlayer = false;
610
+ this.isupdate = false;
705
611
  document.exitPointerLock();
706
612
  window.removeEventListener("keydown", this._boundOnKeydown);
707
613
  window.removeEventListener("keyup", this._boundOnKeyup);
@@ -736,7 +642,7 @@ var PlayerController = class {
736
642
  }
737
643
  this.scene.traverse((c) => {
738
644
  const mesh = c;
739
- if (mesh?.isMesh && mesh.geometry && c.name !== "\u89D2\u8272\u80F6\u56CA\u4F53") {
645
+ if (mesh?.isMesh && mesh.geometry && c.name !== "capsule") {
740
646
  try {
741
647
  let geom = mesh.geometry.clone();
742
648
  geom.applyMatrix4(mesh.matrixWorld);
@@ -762,8 +668,7 @@ var PlayerController = class {
762
668
  attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });
763
669
  } else {
764
670
  const m = attrMap.get(name);
765
- if (m.itemSize !== itemSize || m.arrayCtor !== ctor)
766
- attrConflict.add(name);
671
+ if (m.itemSize !== itemSize || m.arrayCtor !== ctor) attrConflict.add(name);
767
672
  else m.examples++;
768
673
  }
769
674
  }
@@ -784,10 +689,7 @@ var PlayerController = class {
784
689
  const meta = attrMap.get(name);
785
690
  const len = count * meta.itemSize;
786
691
  const array = new meta.arrayCtor(len);
787
- g.setAttribute(
788
- name,
789
- new THREE.BufferAttribute(array, meta.itemSize)
790
- );
692
+ g.setAttribute(name, new THREE.BufferAttribute(array, meta.itemSize));
791
693
  }
792
694
  }
793
695
  }
@@ -810,11 +712,16 @@ var PlayerController = class {
810
712
  merged.boundsTree = new MeshBVH(merged);
811
713
  this.collider = new THREE.Mesh(
812
714
  merged,
715
+ // new THREE.MeshBasicMaterial({
716
+ // color: "red",
717
+ // opacity: 0.2,
718
+ // transparent: true,
719
+ // wireframe: false,
720
+ // })
813
721
  new THREE.MeshBasicMaterial({
814
- color: "red",
815
- opacity: 0.2,
722
+ opacity: 0.5,
816
723
  transparent: true,
817
- wireframe: false
724
+ wireframe: true
818
725
  })
819
726
  );
820
727
  if (this.displayCollider) this.scene.add(this.collider);
@@ -827,7 +734,7 @@ var PlayerController = class {
827
734
  console.log("bvh\u52A0\u8F7D\u6A21\u578B\u6210\u529F", this.collider);
828
735
  }
829
736
  };
830
- function usePlayer() {
737
+ function playerController() {
831
738
  if (!controllerInstance) controllerInstance = new PlayerController();
832
739
  const c = controllerInstance;
833
740
  return {
@@ -836,8 +743,11 @@ function usePlayer() {
836
743
  createBVH: (url = "") => c.createBVH(url),
837
744
  createPlayer: () => c.createPlayer(),
838
745
  reset: (pos) => c.reset(pos),
839
- updatePlayer: (dt) => c.updatePlayer(dt),
840
- destroy: () => c.destroy()
746
+ update: (dt) => c.update(dt),
747
+ destroy: () => c.destroy(),
748
+ displayCollider: c.displayCollider,
749
+ displayPlayer: c.displayPlayer,
750
+ displayVisualizer: c.displayVisualizer
841
751
  };
842
752
  }
843
753
  function onAllEvent() {
@@ -851,6 +761,6 @@ function offAllEvent() {
851
761
  export {
852
762
  offAllEvent,
853
763
  onAllEvent,
854
- usePlayer
764
+ playerController
855
765
  };
856
766
  //# sourceMappingURL=index.mjs.map