three-player-controller 0.3.1 → 0.3.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/LICENSE +1 -1
- package/README.md +7 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +87 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -63
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ export function playerController(): {
|
|
|
73
73
|
reset: (pos?: THREE.Vector3) => void;
|
|
74
74
|
update: (dt?: number) => void;
|
|
75
75
|
destroy: () => void;
|
|
76
|
+
getposition: () => THREE.Vector3;
|
|
76
77
|
};
|
|
77
78
|
```
|
|
78
79
|
|
|
@@ -88,6 +89,8 @@ export function playerController(): {
|
|
|
88
89
|
每帧调用。
|
|
89
90
|
- `destroy()`
|
|
90
91
|
销毁控制器。
|
|
92
|
+
- `getposition()`
|
|
93
|
+
获取人物当前位置。
|
|
91
94
|
|
|
92
95
|
---
|
|
93
96
|
|
|
@@ -139,6 +142,9 @@ type PlayerControllerOptions = {
|
|
|
139
142
|
minCamDistance?: number;
|
|
140
143
|
maxCamDistance?: number;
|
|
141
144
|
colliderMeshUrl?: string;
|
|
145
|
+
isShowMobileControls?: boolean;
|
|
146
|
+
thirdMouseMode?: number;
|
|
147
|
+
enableZoom?: boolean;
|
|
142
148
|
};
|
|
143
149
|
```
|
|
144
150
|
|
|
@@ -161,6 +167,7 @@ type PlayerControllerOptions = {
|
|
|
161
167
|
| `colliderMeshUrl` | `string` | 自制碰撞体模型路径,默认`""` |
|
|
162
168
|
| `isShowMobileControls` | `boolean` | 移动端运行时,是否自动显示移动端控制器,默认`true` |
|
|
163
169
|
| `thirdMouseMode` | `[0, 1, 2, 3]` | 第三人称视角下的不同鼠标控制模式 ,默认`1`(0: 隐藏鼠标控制朝向及视角,1: 隐藏鼠标仅控制视角,2: 显示鼠标拖拽控制朝向及视角, 3: 显示鼠标拖拽仅控制视角) |
|
|
170
|
+
| `enableZoom` | `boolean` | 第三人称模式下是否允许缩放,默认`false` |
|
|
164
171
|
|
|
165
172
|
---
|
|
166
173
|
|
package/dist/index.d.mts
CHANGED
|
@@ -29,6 +29,7 @@ type PlayerControllerOptions = {
|
|
|
29
29
|
colliderMeshUrl?: string;
|
|
30
30
|
isShowMobileControls?: boolean;
|
|
31
31
|
thirdMouseMode?: 0 | 1 | 2 | 3;
|
|
32
|
+
enableZoom?: boolean;
|
|
32
33
|
};
|
|
33
34
|
declare function playerController(): {
|
|
34
35
|
init: (opts: PlayerControllerOptions, callback?: () => void) => Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ type PlayerControllerOptions = {
|
|
|
29
29
|
colliderMeshUrl?: string;
|
|
30
30
|
isShowMobileControls?: boolean;
|
|
31
31
|
thirdMouseMode?: 0 | 1 | 2 | 3;
|
|
32
|
+
enableZoom?: boolean;
|
|
32
33
|
};
|
|
33
34
|
declare function playerController(): {
|
|
34
35
|
init: (opts: PlayerControllerOptions, callback?: () => void) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,6 @@ var PlayerController = class {
|
|
|
60
60
|
constructor() {
|
|
61
61
|
// ==================== 基本配置与参数 ====================
|
|
62
62
|
this.loader = new import_GLTFLoader.GLTFLoader();
|
|
63
|
-
// 0: 普通 1: 飞行 2: 车辆
|
|
64
63
|
// ==================== 玩家基本属性 ====================
|
|
65
64
|
this.playerRadius = 45;
|
|
66
65
|
this.playerHeight = 180;
|
|
@@ -351,6 +350,7 @@ var PlayerController = class {
|
|
|
351
350
|
this._maxCamDistance = (opts.maxCamDistance ?? 440) * s;
|
|
352
351
|
this.orginMaxCamDistance = this._maxCamDistance;
|
|
353
352
|
this.thirdMouseMode = opts.thirdMouseMode ?? 1;
|
|
353
|
+
this.enableZoom = opts.enableZoom ?? false;
|
|
354
354
|
const isMobileDevice = () => navigator.maxTouchPoints && navigator.maxTouchPoints > 0 || "ontouchstart" in window || /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
355
355
|
this.isShowMobileControls = (opts.isShowMobileControls ?? true) && isMobileDevice();
|
|
356
356
|
if (this.isShowMobileControls) {
|
|
@@ -561,6 +561,7 @@ var PlayerController = class {
|
|
|
561
561
|
this.player.attach(this.camera);
|
|
562
562
|
this.camera.position.set(0, 40 * this.playerModel.scale, 30 * this.playerModel.scale);
|
|
563
563
|
this.camera.rotation.set(0, Math.PI, 0);
|
|
564
|
+
this.controls.enableZoom = false;
|
|
564
565
|
} else {
|
|
565
566
|
this.scene.attach(this.camera);
|
|
566
567
|
const worldPos = this.player.position.clone();
|
|
@@ -569,6 +570,7 @@ var PlayerController = class {
|
|
|
569
570
|
const offset = new THREE.Vector3(Math.cos(angle) * 400 * this.playerModel.scale, 200 * this.playerModel.scale, Math.sin(angle) * 400 * this.playerModel.scale);
|
|
570
571
|
this.camera.position.copy(worldPos).add(offset);
|
|
571
572
|
this.controls.target.copy(worldPos);
|
|
573
|
+
this.controls.enableZoom = this.enableZoom;
|
|
572
574
|
}
|
|
573
575
|
this.setPointerLock();
|
|
574
576
|
}
|
|
@@ -582,6 +584,8 @@ var PlayerController = class {
|
|
|
582
584
|
setPointerLock() {
|
|
583
585
|
if ((this.thirdMouseMode === 0 || this.thirdMouseMode === 1) && !this.isFirstPerson || this.isFirstPerson) {
|
|
584
586
|
document.body.requestPointerLock();
|
|
587
|
+
} else {
|
|
588
|
+
document.exitPointerLock();
|
|
585
589
|
}
|
|
586
590
|
}
|
|
587
591
|
/**
|
|
@@ -603,9 +607,10 @@ var PlayerController = class {
|
|
|
603
607
|
* 设置控制器
|
|
604
608
|
*/
|
|
605
609
|
setControls() {
|
|
606
|
-
this.controls.
|
|
610
|
+
this.controls.enableZoom = this.enableZoom;
|
|
607
611
|
this.controls.rotateSpeed = this.mouseSensity * 0.05;
|
|
608
612
|
this.controls.maxPolarAngle = Math.PI * (300 / 360);
|
|
613
|
+
this.controls.mouseButtons = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
|
|
609
614
|
}
|
|
610
615
|
/**
|
|
611
616
|
* 重置控制器
|
|
@@ -648,6 +653,61 @@ var PlayerController = class {
|
|
|
648
653
|
}
|
|
649
654
|
}
|
|
650
655
|
// ==================== 物理与碰撞检测 ====================
|
|
656
|
+
/**
|
|
657
|
+
* 统一属性集合
|
|
658
|
+
*/
|
|
659
|
+
unifiedAttribute(collected) {
|
|
660
|
+
const attrMap = /* @__PURE__ */ new Map();
|
|
661
|
+
const attrConflict = /* @__PURE__ */ new Set();
|
|
662
|
+
const requiredAttrs = /* @__PURE__ */ new Set(["position", "normal", "uv"]);
|
|
663
|
+
for (const g of collected) {
|
|
664
|
+
const attrNames2 = Object.keys(g.attributes);
|
|
665
|
+
for (const name of attrNames2) {
|
|
666
|
+
if (!requiredAttrs.has(name)) {
|
|
667
|
+
g.deleteAttribute(name);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
for (const g of collected) {
|
|
672
|
+
for (const name of Object.keys(g.attributes)) {
|
|
673
|
+
const attr = g.attributes[name];
|
|
674
|
+
const ctor = attr.array.constructor;
|
|
675
|
+
const itemSize = attr.itemSize;
|
|
676
|
+
const normalized = attr.normalized;
|
|
677
|
+
if (!attrMap.has(name)) {
|
|
678
|
+
attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1, normalized });
|
|
679
|
+
} else {
|
|
680
|
+
const m = attrMap.get(name);
|
|
681
|
+
if (m.itemSize !== itemSize || m.arrayCtor !== ctor || m.normalized !== normalized) {
|
|
682
|
+
attrConflict.add(name);
|
|
683
|
+
} else {
|
|
684
|
+
m.examples++;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (attrConflict.size) {
|
|
690
|
+
for (const g of collected) {
|
|
691
|
+
for (const name of Array.from(attrConflict)) {
|
|
692
|
+
if (g.attributes[name]) g.deleteAttribute(name);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
for (const name of attrConflict) attrMap.delete(name);
|
|
696
|
+
}
|
|
697
|
+
const attrNames = Array.from(attrMap.keys());
|
|
698
|
+
for (const g of collected) {
|
|
699
|
+
const count = g.attributes.position.count;
|
|
700
|
+
for (const name of attrNames) {
|
|
701
|
+
if (!g.attributes[name]) {
|
|
702
|
+
const meta = attrMap.get(name);
|
|
703
|
+
const len = count * meta.itemSize;
|
|
704
|
+
const array = new meta.arrayCtor(len);
|
|
705
|
+
g.setAttribute(name, new THREE.BufferAttribute(array, meta.itemSize, meta.normalized));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return collected;
|
|
710
|
+
}
|
|
651
711
|
/**
|
|
652
712
|
* BVH碰撞体构建
|
|
653
713
|
*/
|
|
@@ -663,7 +723,7 @@ var PlayerController = class {
|
|
|
663
723
|
}
|
|
664
724
|
return geom;
|
|
665
725
|
};
|
|
666
|
-
|
|
726
|
+
let collected = [];
|
|
667
727
|
if (meshUrl === "") {
|
|
668
728
|
if (this.collider) {
|
|
669
729
|
this.scene.remove(this.collider);
|
|
@@ -684,45 +744,7 @@ var PlayerController = class {
|
|
|
684
744
|
}
|
|
685
745
|
});
|
|
686
746
|
if (!collected.length) return;
|
|
687
|
-
|
|
688
|
-
const attrConflict = /* @__PURE__ */ new Set();
|
|
689
|
-
for (const g of collected) {
|
|
690
|
-
for (const name of Object.keys(g.attributes)) {
|
|
691
|
-
const attr = g.attributes[name];
|
|
692
|
-
const ctor = attr.array.constructor;
|
|
693
|
-
const itemSize = attr.itemSize;
|
|
694
|
-
if (!attrMap.has(name)) {
|
|
695
|
-
attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });
|
|
696
|
-
} else {
|
|
697
|
-
const m = attrMap.get(name);
|
|
698
|
-
if (m.itemSize !== itemSize || m.arrayCtor !== ctor) {
|
|
699
|
-
attrConflict.add(name);
|
|
700
|
-
} else {
|
|
701
|
-
m.examples++;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
if (attrConflict.size) {
|
|
707
|
-
for (const g of collected) {
|
|
708
|
-
for (const name of Array.from(attrConflict)) {
|
|
709
|
-
if (g.attributes[name]) g.deleteAttribute(name);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
for (const name of attrConflict) attrMap.delete(name);
|
|
713
|
-
}
|
|
714
|
-
const attrNames = Array.from(attrMap.keys());
|
|
715
|
-
for (const g of collected) {
|
|
716
|
-
const count = g.attributes.position.count;
|
|
717
|
-
for (const name of attrNames) {
|
|
718
|
-
if (!g.attributes[name]) {
|
|
719
|
-
const meta = attrMap.get(name);
|
|
720
|
-
const len = count * meta.itemSize;
|
|
721
|
-
const array = new meta.arrayCtor(len);
|
|
722
|
-
g.setAttribute(name, new THREE.BufferAttribute(array, meta.itemSize));
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
747
|
+
collected = this.unifiedAttribute(collected);
|
|
726
748
|
} else {
|
|
727
749
|
const gltf = await this.loader.loadAsync(meshUrl);
|
|
728
750
|
const mesh = gltf.scene.children[0];
|
|
@@ -912,28 +934,30 @@ var PlayerController = class {
|
|
|
912
934
|
this.controls.target.copy(lookTarget);
|
|
913
935
|
this.camera.position.add(lookTarget);
|
|
914
936
|
this.controls.update();
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
937
|
+
if (!this.enableZoom) {
|
|
938
|
+
this._personToCam.subVectors(this.camera.position, this.player.position);
|
|
939
|
+
const origin = this.player.position.clone().add(new THREE.Vector3(0, 0, 0));
|
|
940
|
+
const direction = this._personToCam.clone().normalize();
|
|
941
|
+
const desiredDist = this._personToCam.length();
|
|
942
|
+
this._raycasterPersonToCam.set(origin, direction);
|
|
943
|
+
this._raycasterPersonToCam.far = desiredDist;
|
|
944
|
+
const intersects2 = this._raycasterPersonToCam.intersectObject(this.collider, false);
|
|
945
|
+
if (intersects2.length > 0) {
|
|
946
|
+
const hit = intersects2[0];
|
|
947
|
+
const safeDist = Math.max(hit.distance - this._camEpsilon, this._minCamDistance);
|
|
948
|
+
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
949
|
+
this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
|
|
950
|
+
} else {
|
|
951
|
+
this._raycasterPersonToCam.far = this._maxCamDistance;
|
|
952
|
+
const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(this.collider, false);
|
|
953
|
+
let safeDist = this._maxCamDistance;
|
|
954
|
+
if (intersectsMaxDis.length) {
|
|
955
|
+
const hitMax = intersectsMaxDis[0];
|
|
956
|
+
safeDist = hitMax.distance - this._camEpsilon;
|
|
957
|
+
}
|
|
958
|
+
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
959
|
+
this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
|
|
934
960
|
}
|
|
935
|
-
const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
|
|
936
|
-
this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
|
|
937
961
|
}
|
|
938
962
|
}
|
|
939
963
|
if (this.player.position.y < this.boundingBoxMinY - 1) {
|