three-player-controller 0.3.0 → 0.3.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/LICENSE +1 -1
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +119 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -23,7 +23,7 @@ var PlayerController = class {
|
|
|
23
23
|
constructor() {
|
|
24
24
|
// ==================== 基本配置与参数 ====================
|
|
25
25
|
this.loader = new GLTFLoader();
|
|
26
|
-
// 0:
|
|
26
|
+
// 0: 普通 1: 飞行 2: 车辆
|
|
27
27
|
// ==================== 玩家基本属性 ====================
|
|
28
28
|
this.playerRadius = 45;
|
|
29
29
|
this.playerHeight = 180;
|
|
@@ -37,6 +37,7 @@ var PlayerController = class {
|
|
|
37
37
|
this.collider = null;
|
|
38
38
|
this.visualizer = null;
|
|
39
39
|
this.person = null;
|
|
40
|
+
this.vehicle = null;
|
|
40
41
|
// ==================== 状态开关 ====================
|
|
41
42
|
this.playerIsOnGround = false;
|
|
42
43
|
this.isupdate = true;
|
|
@@ -199,6 +200,9 @@ var PlayerController = class {
|
|
|
199
200
|
this.playPersonAnimationByName("jumping");
|
|
200
201
|
}
|
|
201
202
|
break;
|
|
203
|
+
case "KeyE":
|
|
204
|
+
this.setDrive();
|
|
205
|
+
break;
|
|
202
206
|
}
|
|
203
207
|
};
|
|
204
208
|
/**
|
|
@@ -466,6 +470,50 @@ var PlayerController = class {
|
|
|
466
470
|
this.reset();
|
|
467
471
|
this.player.rotateY(this.playerModel.rotateY ?? 0);
|
|
468
472
|
}
|
|
473
|
+
// ==================== 车辆模型相关 ====================
|
|
474
|
+
/**
|
|
475
|
+
* 加载车辆模型与动画
|
|
476
|
+
*/
|
|
477
|
+
async loadVehicleModel(params) {
|
|
478
|
+
try {
|
|
479
|
+
const { url, position, scale = 1 } = params;
|
|
480
|
+
const gltf = await this.loader.loadAsync(url);
|
|
481
|
+
this.vehicle = gltf.scene;
|
|
482
|
+
this.vehicle.scale.set(scale, scale, scale);
|
|
483
|
+
this.vehicle.position.copy(position);
|
|
484
|
+
this.vehicle.traverse((child) => {
|
|
485
|
+
if (child.isMesh) {
|
|
486
|
+
child.castShadow = true;
|
|
487
|
+
child.receiveShadow = true;
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
this.scene.add(this.vehicle);
|
|
491
|
+
const animations = gltf.animations ?? [];
|
|
492
|
+
this.vehicleActions = /* @__PURE__ */ new Map();
|
|
493
|
+
this.vehicleMixer = new THREE.AnimationMixer(this.vehicle);
|
|
494
|
+
const animationMappings = [
|
|
495
|
+
[params.animations?.openDoorAnim ?? "", "open_door"],
|
|
496
|
+
[params.animations?.wheelsTurnAnim ?? "", "wheels_turn"],
|
|
497
|
+
[params.animations?.turnLeftAnim ?? "", "turn_left"],
|
|
498
|
+
[params.animations?.turnRightAnim ?? "", "turn_right"]
|
|
499
|
+
];
|
|
500
|
+
const findClip = (name) => animations.find((a) => a.name === name);
|
|
501
|
+
for (const [clipName, actionName] of animationMappings) {
|
|
502
|
+
const clip = findClip(clipName);
|
|
503
|
+
if (!clip) continue;
|
|
504
|
+
const action = this.vehicleMixer.clipAction(clip);
|
|
505
|
+
action.setLoop(THREE.LoopOnce, 1);
|
|
506
|
+
action.clampWhenFinished = true;
|
|
507
|
+
action.setEffectiveTimeScale(2);
|
|
508
|
+
action.enabled = true;
|
|
509
|
+
action.setEffectiveWeight(0);
|
|
510
|
+
this.vehicleActions.set(actionName, action);
|
|
511
|
+
}
|
|
512
|
+
console.log("\u5F00\u95E8\u52A8\u753B", this.vehicleActions.get("open_door"));
|
|
513
|
+
} catch (error) {
|
|
514
|
+
console.error("\u52A0\u8F7D\u8F66\u8F86\u6A21\u578B\u5931\u8D25:", error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
469
517
|
// ==================== 相机与视角控制 ====================
|
|
470
518
|
/**
|
|
471
519
|
* 第一/三人称视角切换
|
|
@@ -487,6 +535,10 @@ var PlayerController = class {
|
|
|
487
535
|
}
|
|
488
536
|
this.setPointerLock();
|
|
489
537
|
}
|
|
538
|
+
setDrive() {
|
|
539
|
+
this.controllerMode = 2;
|
|
540
|
+
this.person?.attach(this.vehicle);
|
|
541
|
+
}
|
|
490
542
|
/**
|
|
491
543
|
* 设置指针锁定
|
|
492
544
|
*/
|
|
@@ -559,6 +611,61 @@ var PlayerController = class {
|
|
|
559
611
|
}
|
|
560
612
|
}
|
|
561
613
|
// ==================== 物理与碰撞检测 ====================
|
|
614
|
+
/**
|
|
615
|
+
* 统一属性集合
|
|
616
|
+
*/
|
|
617
|
+
unifiedAttribute(collected) {
|
|
618
|
+
const attrMap = /* @__PURE__ */ new Map();
|
|
619
|
+
const attrConflict = /* @__PURE__ */ new Set();
|
|
620
|
+
const requiredAttrs = /* @__PURE__ */ new Set(["position", "normal", "uv"]);
|
|
621
|
+
for (const g of collected) {
|
|
622
|
+
const attrNames2 = Object.keys(g.attributes);
|
|
623
|
+
for (const name of attrNames2) {
|
|
624
|
+
if (!requiredAttrs.has(name)) {
|
|
625
|
+
g.deleteAttribute(name);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
for (const g of collected) {
|
|
630
|
+
for (const name of Object.keys(g.attributes)) {
|
|
631
|
+
const attr = g.attributes[name];
|
|
632
|
+
const ctor = attr.array.constructor;
|
|
633
|
+
const itemSize = attr.itemSize;
|
|
634
|
+
const normalized = attr.normalized;
|
|
635
|
+
if (!attrMap.has(name)) {
|
|
636
|
+
attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1, normalized });
|
|
637
|
+
} else {
|
|
638
|
+
const m = attrMap.get(name);
|
|
639
|
+
if (m.itemSize !== itemSize || m.arrayCtor !== ctor || m.normalized !== normalized) {
|
|
640
|
+
attrConflict.add(name);
|
|
641
|
+
} else {
|
|
642
|
+
m.examples++;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (attrConflict.size) {
|
|
648
|
+
for (const g of collected) {
|
|
649
|
+
for (const name of Array.from(attrConflict)) {
|
|
650
|
+
if (g.attributes[name]) g.deleteAttribute(name);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
for (const name of attrConflict) attrMap.delete(name);
|
|
654
|
+
}
|
|
655
|
+
const attrNames = Array.from(attrMap.keys());
|
|
656
|
+
for (const g of collected) {
|
|
657
|
+
const count = g.attributes.position.count;
|
|
658
|
+
for (const name of attrNames) {
|
|
659
|
+
if (!g.attributes[name]) {
|
|
660
|
+
const meta = attrMap.get(name);
|
|
661
|
+
const len = count * meta.itemSize;
|
|
662
|
+
const array = new meta.arrayCtor(len);
|
|
663
|
+
g.setAttribute(name, new THREE.BufferAttribute(array, meta.itemSize, meta.normalized));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return collected;
|
|
668
|
+
}
|
|
562
669
|
/**
|
|
563
670
|
* BVH碰撞体构建
|
|
564
671
|
*/
|
|
@@ -574,7 +681,7 @@ var PlayerController = class {
|
|
|
574
681
|
}
|
|
575
682
|
return geom;
|
|
576
683
|
};
|
|
577
|
-
|
|
684
|
+
let collected = [];
|
|
578
685
|
if (meshUrl === "") {
|
|
579
686
|
if (this.collider) {
|
|
580
687
|
this.scene.remove(this.collider);
|
|
@@ -595,45 +702,7 @@ var PlayerController = class {
|
|
|
595
702
|
}
|
|
596
703
|
});
|
|
597
704
|
if (!collected.length) return;
|
|
598
|
-
|
|
599
|
-
const attrConflict = /* @__PURE__ */ new Set();
|
|
600
|
-
for (const g of collected) {
|
|
601
|
-
for (const name of Object.keys(g.attributes)) {
|
|
602
|
-
const attr = g.attributes[name];
|
|
603
|
-
const ctor = attr.array.constructor;
|
|
604
|
-
const itemSize = attr.itemSize;
|
|
605
|
-
if (!attrMap.has(name)) {
|
|
606
|
-
attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1 });
|
|
607
|
-
} else {
|
|
608
|
-
const m = attrMap.get(name);
|
|
609
|
-
if (m.itemSize !== itemSize || m.arrayCtor !== ctor) {
|
|
610
|
-
attrConflict.add(name);
|
|
611
|
-
} else {
|
|
612
|
-
m.examples++;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
if (attrConflict.size) {
|
|
618
|
-
for (const g of collected) {
|
|
619
|
-
for (const name of Array.from(attrConflict)) {
|
|
620
|
-
if (g.attributes[name]) g.deleteAttribute(name);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
for (const name of attrConflict) attrMap.delete(name);
|
|
624
|
-
}
|
|
625
|
-
const attrNames = Array.from(attrMap.keys());
|
|
626
|
-
for (const g of collected) {
|
|
627
|
-
const count = g.attributes.position.count;
|
|
628
|
-
for (const name of attrNames) {
|
|
629
|
-
if (!g.attributes[name]) {
|
|
630
|
-
const meta = attrMap.get(name);
|
|
631
|
-
const len = count * meta.itemSize;
|
|
632
|
-
const array = new meta.arrayCtor(len);
|
|
633
|
-
g.setAttribute(name, new THREE.BufferAttribute(array, meta.itemSize));
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
}
|
|
705
|
+
collected = this.unifiedAttribute(collected);
|
|
637
706
|
} else {
|
|
638
707
|
const gltf = await this.loader.loadAsync(meshUrl);
|
|
639
708
|
const mesh = gltf.scene.children[0];
|
|
@@ -729,8 +798,12 @@ var PlayerController = class {
|
|
|
729
798
|
this.player.position.addScaledVector(this.playerVelocity, delta);
|
|
730
799
|
this.playerIsOnGround = true;
|
|
731
800
|
} else {
|
|
732
|
-
this.
|
|
733
|
-
|
|
801
|
+
if (this.spacePressed) {
|
|
802
|
+
this.playerVelocity.y += delta * this.gravity;
|
|
803
|
+
} else {
|
|
804
|
+
this.playerVelocity.set(0, 0, 0);
|
|
805
|
+
this.playerIsOnGround = true;
|
|
806
|
+
}
|
|
734
807
|
}
|
|
735
808
|
} else if (playerDistanceFromGround > minH && playerDistanceFromGround < h) {
|
|
736
809
|
this.playerVelocity.set(0, 0, 0);
|
|
@@ -861,6 +934,7 @@ var PlayerController = class {
|
|
|
861
934
|
*/
|
|
862
935
|
updateMixers(delta) {
|
|
863
936
|
if (this.personMixer) this.personMixer.update(delta);
|
|
937
|
+
if (this.vehicleMixer) this.vehicleMixer.update(delta);
|
|
864
938
|
}
|
|
865
939
|
/**
|
|
866
940
|
* 重置玩家位置
|
|
@@ -1183,7 +1257,8 @@ function playerController() {
|
|
|
1183
1257
|
update: (dt) => c.update(dt),
|
|
1184
1258
|
destroy: () => c.destroy(),
|
|
1185
1259
|
setInput: (i) => c.setInput(i),
|
|
1186
|
-
getposition: () => c.getPosition()
|
|
1260
|
+
getposition: () => c.getPosition(),
|
|
1261
|
+
loadVehicleModel: (params) => c.loadVehicleModel(params)
|
|
1187
1262
|
};
|
|
1188
1263
|
}
|
|
1189
1264
|
function onAllEvent() {
|