three-player-controller 0.3.8 → 0.4.0

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,11 +8,7 @@
8
8
 
9
9
  # 示例
10
10
 
11
- - [glb 场景](https://hh-hang.github.io/three-player-controller/index.html)
12
-
13
- - [3dtiles 场景](https://hh-hang.github.io/three-player-controller/3dtilesScene.html)
14
-
15
- - [3dtiles 自定义](https://hh-hang.github.io/three-player-controller/3dtilesCustomize.html)
11
+ - [示例](https://hh-hang.github.io/three-player-controller/index.html)
16
12
 
17
13
  ### 普通控制
18
14
 
@@ -256,7 +252,7 @@ type PlayerControllerOptions = {
256
252
  gravity?: number;
257
253
  jumpHeight?: number;
258
254
  speed?: number;
259
- playerFlySpeed?: number;
255
+ flySpeed?: number;
260
256
  rotateY?: number;
261
257
  headObjName?: string;
262
258
  flyEnabled?: boolean;
@@ -299,7 +295,7 @@ type PlayerControllerOptions = {
299
295
  | `playerModel.speed` | `number` | 否 | `300` | 移动速度基准值 |
300
296
  | `playerModel.gravity` | `number` | 否 | `-2400` | 重力加速度基准值 |
301
297
  | `playerModel.jumpHeight` | `number` | 否 | `600` | 跳跃高度基准值 |
302
- | `playerModel.playerFlySpeed` | `number` | 否 | `2100` | 飞行速度基准值 |
298
+ | `playerModel.flySpeed` | `number` | 否 | `2100` | 飞行速度基准值 |
303
299
  | `playerModel.flyEnabled` | `boolean` | 否 | `true` | 是否允许飞行模式 |
304
300
  | `initPos` | `THREE.Vector3` | 否 | `(0,0,0)` | 初始位置 |
305
301
  | `mouseSensity` | `number` | 否 | `5` | 鼠标灵敏度 |
@@ -416,4 +412,4 @@ player.playAnimation("attack", { force: true });
416
412
  [three](https://github.com/mrdoob/three.js)
417
413
 
418
414
  [npm]: https://img.shields.io/npm/v/three-player-controller
419
- [npm-url]: https://www.npmjs.com/package/three-player-controller
415
+ [npm-url]: https://www.npmjs.com/package/three-player-controller
package/dist/index.d.mts CHANGED
@@ -109,13 +109,23 @@ declare function playerController(): {
109
109
  update: (dt?: number) => Promise<void>;
110
110
  destroy: () => void;
111
111
  reset: (pos?: THREE.Vector3) => void;
112
- setInput: (i: any) => void;
112
+ setInput: (i: Parameters<(input: Partial<{
113
+ moveX: 1 | 0 | -1;
114
+ moveY: 1 | 0 | -1;
115
+ lookDeltaX: number;
116
+ lookDeltaY: number;
117
+ jump: boolean;
118
+ shift: boolean;
119
+ toggleView: boolean;
120
+ toggleFly: boolean;
121
+ toggleVehicle: boolean;
122
+ }>) => void>[0]) => void;
113
123
  changeView: () => void;
114
124
  getPosition: () => THREE.Vector3;
115
- getCenterScreenRaycastHit: () => THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>>;
116
- getCurrentPersonAnimationName: () => string;
117
- getPerson: () => THREE.Object3D<THREE.Object3DEventMap>;
118
- getActiveVehicle: () => VehicleInstance;
125
+ getCenterScreenRaycastHit: () => THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>> | undefined;
126
+ getCurrentPersonAnimationName: () => string | null;
127
+ getPerson: () => THREE.Object3D<THREE.Object3DEventMap> | null;
128
+ getActiveVehicle: () => VehicleInstance | null;
119
129
  getAllVehicles: () => VehicleInstance[];
120
130
  switchPlayerModel: (model: PlayerModelOptions) => Promise<void>;
121
131
  setPlayerScale: (scale: number) => void;
package/dist/index.d.ts CHANGED
@@ -109,13 +109,23 @@ declare function playerController(): {
109
109
  update: (dt?: number) => Promise<void>;
110
110
  destroy: () => void;
111
111
  reset: (pos?: THREE.Vector3) => void;
112
- setInput: (i: any) => void;
112
+ setInput: (i: Parameters<(input: Partial<{
113
+ moveX: 1 | 0 | -1;
114
+ moveY: 1 | 0 | -1;
115
+ lookDeltaX: number;
116
+ lookDeltaY: number;
117
+ jump: boolean;
118
+ shift: boolean;
119
+ toggleView: boolean;
120
+ toggleFly: boolean;
121
+ toggleVehicle: boolean;
122
+ }>) => void>[0]) => void;
113
123
  changeView: () => void;
114
124
  getPosition: () => THREE.Vector3;
115
- getCenterScreenRaycastHit: () => THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>>;
116
- getCurrentPersonAnimationName: () => string;
117
- getPerson: () => THREE.Object3D<THREE.Object3DEventMap>;
118
- getActiveVehicle: () => VehicleInstance;
125
+ getCenterScreenRaycastHit: () => THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>> | undefined;
126
+ getCurrentPersonAnimationName: () => string | null;
127
+ getPerson: () => THREE.Object3D<THREE.Object3DEventMap> | null;
128
+ getActiveVehicle: () => VehicleInstance | null;
119
129
  getAllVehicles: () => VehicleInstance[];
120
130
  switchPlayerModel: (model: PlayerModelOptions) => Promise<void>;
121
131
  setPlayerScale: (scale: number) => void;
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1037,13 +1038,12 @@ var PlayerController = class {
1037
1038
  }
1038
1039
  const w = window.innerWidth;
1039
1040
  const h = window.innerHeight;
1040
- this.camera.setViewOffset(w, h, -w * -0.15, 0, w, h);
1041
+ this.camera.setViewOffset(w, h, w * 0.15, 0, w, h);
1041
1042
  }
1042
1043
  // 初始化加载器
1043
1044
  async initLoader() {
1044
1045
  const dracoLoader = new import_DRACOLoader.DRACOLoader();
1045
- dracoLoader.setDecoderPath("https://unpkg.com/three@0.180.0/examples/jsm/libs/draco/gltf/");
1046
- dracoLoader.setDecoderConfig({ type: "js" });
1046
+ dracoLoader.setDecoderPath("https://unpkg.com/three@0.182.0/examples/jsm/libs/draco/gltf/");
1047
1047
  this.loader.setDRACOLoader(dracoLoader);
1048
1048
  }
1049
1049
  // 初始化物理引擎
@@ -1116,12 +1116,12 @@ var PlayerController = class {
1116
1116
  action.setEffectiveWeight(0);
1117
1117
  this.personActions.set(actionName, action);
1118
1118
  }
1119
- this.personActions.get("idle").setEffectiveWeight(1);
1120
- this.personActions.get("idle").play();
1119
+ this.personActions.get("idle")?.setEffectiveWeight(1);
1120
+ this.personActions.get("idle")?.play();
1121
1121
  this.actionState = this.personActions.get("idle");
1122
- this.personMixer.addEventListener("finished", (ev) => {
1122
+ this.personMixerFinishedCb = (ev) => {
1123
1123
  const done = ev.action;
1124
- if (done === this.personActions.get("jumping")) {
1124
+ if (done === this.personActions?.get("jumping")) {
1125
1125
  if (this.fwdPressed) {
1126
1126
  this.playPersonAnimationByName(this.shiftPressed ? "running" : "walking");
1127
1127
  return;
@@ -1137,7 +1137,8 @@ var PlayerController = class {
1137
1137
  this.playPersonAnimationByName("idle");
1138
1138
  }
1139
1139
  if (done === this.personActions?.get("enterCar")) this.onEnterCarAnimFinished();
1140
- });
1140
+ };
1141
+ this.personMixer.addEventListener("finished", this.personMixerFinishedCb);
1141
1142
  this.personMixer.update(0);
1142
1143
  this.person.updateMatrixWorld(true);
1143
1144
  const { size } = this.getBbox(this.person);
@@ -1192,6 +1193,10 @@ var PlayerController = class {
1192
1193
  this.personHead = null;
1193
1194
  }
1194
1195
  if (this.personMixer) {
1196
+ if (this.personMixerFinishedCb) {
1197
+ this.personMixer.removeEventListener("finished", this.personMixerFinishedCb);
1198
+ this.personMixerFinishedCb = void 0;
1199
+ }
1195
1200
  this.personMixer.stopAllAction();
1196
1201
  this.personMixer.uncacheRoot(this.personMixer.getRoot());
1197
1202
  this.personMixer = void 0;
@@ -1442,15 +1447,13 @@ var PlayerController = class {
1442
1447
  changeView() {
1443
1448
  this.isFirstPerson = !this.isFirstPerson;
1444
1449
  if (this.isFirstPerson) {
1445
- const camWorldDir = new THREE4.Vector3();
1446
- this.camera.getWorldDirection(camWorldDir);
1447
- const flatDir = new THREE4.Vector3(camWorldDir.x, 0, camWorldDir.z).normalize();
1450
+ const playerFwd = new THREE4.Vector3(0, 0, 1).applyQuaternion(this.player.quaternion);
1451
+ const flatDir = new THREE4.Vector3(playerFwd.x, 0, playerFwd.z).normalize();
1448
1452
  if (flatDir.lengthSq() > 1e-3) {
1449
1453
  const yAngle = Math.atan2(flatDir.x, flatDir.z);
1450
- this.player.rotation.set(0, yAngle + Math.PI, 0);
1454
+ this.player.rotation.set(0, yAngle, 0);
1451
1455
  }
1452
- const vertAngle = Math.asin(THREE4.MathUtils.clamp(-camWorldDir.y, -1, 1));
1453
- this.setFirstPersonCamera(vertAngle);
1456
+ this.setFirstPersonCamera();
1454
1457
  this.setOverShoulderView(false);
1455
1458
  } else {
1456
1459
  this.controls.enabled = true;
@@ -1648,7 +1651,7 @@ var PlayerController = class {
1648
1651
  return;
1649
1652
  }
1650
1653
  merged.boundsTree = new import_three_mesh_bvh.MeshBVH(merged, { maxDepth: 100 });
1651
- this.collider = new THREE4.Mesh(merged, new THREE4.MeshBasicMaterial({ opacity: 0.5, transparent: true, wireframe: true, depthTest: true }));
1654
+ this.collider = new THREE4.Mesh(merged, new THREE4.MeshBasicMaterial({ opacity: 0.5, transparent: true, wireframe: true, depthTest: true, side: THREE4.DoubleSide }));
1652
1655
  if (this.displayCollider) this.scene.add(this.collider);
1653
1656
  if (this.displayVisualizer) {
1654
1657
  if (this.visualizer) this.scene.remove(this.visualizer);
@@ -1686,7 +1689,7 @@ var PlayerController = class {
1686
1689
  return;
1687
1690
  }
1688
1691
  merged.boundsTree = new import_three_mesh_bvh.MeshBVH(merged);
1689
- this.dynamicCollider = new THREE4.Mesh(merged, new THREE4.MeshBasicMaterial({ opacity: 0.5, transparent: true, wireframe: true, depthTest: true }));
1692
+ this.dynamicCollider = new THREE4.Mesh(merged, new THREE4.MeshBasicMaterial({ opacity: 0.5, transparent: true, wireframe: true, depthTest: true, side: THREE4.DoubleSide }));
1690
1693
  if (this.displayCollider) this.scene.add(this.dynamicCollider);
1691
1694
  }
1692
1695
  // ==================== 控制器过渡 ====================
@@ -1921,16 +1924,17 @@ var PlayerController = class {
1921
1924
  if (!this.spacePressed) {
1922
1925
  this.playerVelocity.set(0, 0, 0);
1923
1926
  this.playerIsOnGround = true;
1924
- this.player.position.y = hits[0].point.y + h;
1927
+ this.player.position.y = THREE4.MathUtils.lerp(this.player.position.y, hits[0].point.y + h, Math.min(1, 15 * delta));
1925
1928
  }
1926
1929
  } else if (dist >= minH) {
1927
1930
  this.playerVelocity.set(0, 0, 0);
1928
1931
  this.playerIsOnGround = true;
1929
1932
  this.player.position.y = hits[0].point.y + h;
1930
1933
  } else {
1934
+ const targetY = hits[0].point.y + h;
1931
1935
  this.playerVelocity.set(0, 0, 0);
1932
- this.player.position.y = hits[0].point.y + h;
1933
1936
  this.playerIsOnGround = true;
1937
+ this.player.position.y = THREE4.MathUtils.lerp(this.player.position.y, targetY, Math.min(1, 15 * delta));
1934
1938
  }
1935
1939
  }
1936
1940
  this.player.updateMatrixWorld();
@@ -1951,7 +1955,14 @@ var PlayerController = class {
1951
1955
  const distance = tri.closestPointToSegment(this.tempSegment, this.tempVector, this.tempVector2);
1952
1956
  if (distance < capsuleInfo.radius) {
1953
1957
  const normal = tri.getNormal(new THREE4.Vector3());
1954
- if (normal.y > 0.5 && !this.isFlying) return;
1958
+ if (!this.isFlying && Math.abs(normal.y) > 0.5) return;
1959
+ if (!this.isFlying && Math.abs(normal.y) <= 0.5) {
1960
+ const e = this.collider.matrixWorld.elements;
1961
+ const contactWorldY = e[1] * this.tempVector.x + e[5] * this.tempVector.y + e[9] * this.tempVector.z + e[13];
1962
+ const s = this.playerModel.scale;
1963
+ const feetY = this.player.position.y - this.playerCapsuleHeight * s * 0.75;
1964
+ if (contactWorldY < feetY + this.playerCapsuleHeight * s * 0.25) return;
1965
+ }
1955
1966
  const dir = this.tempVector2.sub(this.tempVector).normalize();
1956
1967
  const depth = capsuleInfo.radius - distance;
1957
1968
  this.tempSegment.start.addScaledVector(dir, depth);
@@ -2046,13 +2057,14 @@ var PlayerController = class {
2046
2057
  }
2047
2058
  // 屏幕中心射线
2048
2059
  getCenterScreenRaycastHit() {
2060
+ if (!this.collider) return void 0;
2049
2061
  this.camera.updateMatrixWorld();
2050
2062
  this.centerRay.setFromCamera(this.centerMouse, this.camera);
2051
2063
  return this.centerRay.intersectObject(this.collider, false)[0];
2052
2064
  }
2053
2065
  // 获取当前人物动画名称
2054
2066
  getCurrentPersonAnimationName() {
2055
- return this.actionState._clip.name;
2067
+ return this.actionState?.getClip()?.name ?? null;
2056
2068
  }
2057
2069
  // 更新动画混合器
2058
2070
  updateMixers(delta) {
@@ -2219,6 +2231,7 @@ var PlayerController = class {
2219
2231
  for (const v of this.vehicles) {
2220
2232
  this.scene.remove(v.vehicleGroup);
2221
2233
  v.pathPlanner?.dispose();
2234
+ v.vehicleController?.destroy?.();
2222
2235
  }
2223
2236
  this.vehicles = [];
2224
2237
  this.activeVehicle = null;