three-player-controller 0.3.4 → 0.3.6

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,18 @@ 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
 
9
+ // assets/imgs/break.png
10
+ var break_default = "data:image/png;base64,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";
11
+
9
12
  // assets/imgs/fly.png
10
13
  var fly_default = "data:image/png;base64,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";
11
14
 
12
15
  // assets/imgs/jump.png
13
16
  var jump_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAPAUlEQVR4AeydC3rbuA6FnbuxO11Z25V1ZmUe/AmZkWXLliU+APL0E6o3CR7wF0jZSf530T8pIAU2FRAgm9LohBS4XASIeoEUeKKAAHkijk5JAQGiPiAFnihQEZAnteqUFAiigAAJEii52UcBAdJHd9UaRAEBEiRQcrOPAgKkj+6qNYgCMQEJIq7cjK+AAIkfQ7WgogICpKK4Kjq+AgIkfgzVgooKCJCK4qro+AoIkFUMtSsFlgoIkKUa2n6qwPV6/SvZH1tjtrpZOIb9sqO/nhYW5KQACRKonm7S2c3+mA/Z/rJtzFY3C8ewn3b0p93DEhoWAWKR1PJYAevdZIyrnaXD0/Ft8+2Fe4ElJCgC5O14j39DAiNni1IN/galVIEtyhEgLVT+qiPE/wYHcwfgOJoxXrWTbEL5r65zcV6AuAiDDycSHDzpazv0OXRL9dWu61T5AuSUfOPcnDprCziWopFNamWqZT2HtwXIYenGubETHFlA18MtAZLDNOna4OAJ3jpz3KhtPriFRIDchCrqzim/PXRO5iS8HDjVkBo3C5AaqgYp057cnjpl1yy2FTIBsqXMHMdddUpnwH72AAHyKUOb/6wDMJS4sTY139divnjKHtlBV8DilABBhYpmHREg+AIfX9lgvH9jdj4vfBWDCXNFb26K/v/NnpMdE6OlBi9bLUBeSnTsAgs0HT7DsCfoPD0BCWvxdN/j0+VyrPln7kKHM/cXvVeAFJXzcjEwPjOGFfvT7Egn5B4+QKsGiflYrWxr89mF9p8to9j9AqSYlBfgoOORNUoEGUisL19LlFWwlfWLska7abMAKRRvCypw/CxU3LKYGkMul/OPZaO9bAuQApGoCEf2jmwCgHl/9LUyyGARrpE51hIBCcO39fEj+y464BHHW9+jDLJQ3DIBE+zPt0+2zdAGs80ra45/2uKWPO9YHqq5jX9X+zdTNqmp58uypwfEOhudjo5/NbV4QpMNeMJms8MXtjn+aXYPC9Dk6y+N/5FNzkDyd2N/w1Y3NSDWy+lkRzs50GC9gn8Wkl5+h6p3SkAMDLLGUTA8BRhIaIcnn0r44ibDTQcIcFgE6VQ9n/7mQrEF2K1ZV7Lh3kL/2Xthj+s+Pj7OAlLM7akAsV4EFMBRTEBHBZFN9kLipgM+0M+Vb1MBYsFgkm2rYZddkHh6Qj+IhKvsNg0glj14upJBHsRkqENAwhu2V2119aReRMCVX9MAYgEYPXtYE78X4AASHgrfB1cbv1f7HnZ/e8tuUwCSsoeHDtDaB7LJQ0hSR3T1tG4tzp767gHZc1e8a2bKHuvobEJiF3rKImSPhzCbn92W4QGZOHssOxWQMORi6PV9PGURL5C4zGbDA/LdG7QBHEBy85Q2SNjv3TnJHr19eNhDZgBEP/twG3qyCVB8HzVIfthOrw4KHDf+mC9ulhkA4cnpRnAnjtxBYn71GGq5hsM0uTQFhAplbhS4gcSyyN9mH+Zdq0ziHg7TYgpAWgUcPaMZkNxkWIOE4VbtbBICDoKpDIIKc9vdK3CDhDlBDUh4WP1I5YdQXYCECFNVJ/k28E0WoTY6sRlDrhKgZDCAg22qCGEzAFIiwCGCecLJuyySyzJIfpllUN7REhC4Higw9nOxYdajABJGcKeOPswiS18NEkDBgIV5CgYAS+MYMNjlH6y5PiQYue3DA/Lx9cM3oYOUg1V5vZlF1vWiaTIAWBpvwobSenhAUnB5yqVNrTYUuJuHbFw31eEpAOFpN1VUDzb2er0KkpV2UwCS2sz4OG1qtaHA7mHWxv3DHZ4GkJRFDgy1hou5GvSGAtMAgiYGCR+ADTWJpF0FTUOslZhTAULbDRKGWoIEMWQvFZgOEBRJkGi4hRiypwpMCQiKGCQMtwQJYsg2FZgWEBQBEjM+GQYUjMPtTDW5V2BqQHJ0DJL8afDssGhuljtFWguQJEReLWGx7dmAcfVbDXNMeq4FyAv1DZJlduENGEMx7MWdOj2CAgLkjSgaLHwZ7xEwGpq8oWOkSwXIiWgtgOGr3cvhWHdgDjYrqt8Hm/v6NgHyWqPdVxgwObusgdldRs8LzX8BsgqAAFkJUnLXOlwGxjY/lhmmZDUqq6ICAqSiuOuiPz4+PAOj7LEOmO0LEBOh17IExnzo3UH1iteCsF4EyFqRfvtROmg/hTrULEA6iL5RZe/fIdw7g23I0vewAOmr/7L2rj+LYcM9AbKMRtoWIEkIraTAIwUEyCNVGh9z8MsSlD02Yi5ANoRpfLjr8Mra6uQFgXnibBEgzgIid3wpIEB8xUPeOFNAgPgIiF7x+ojDnRcC5E6SLge6zkH0inc75gJkWxudGUmBg20RIAeFK3WbXvGWUrJOOQKkjq7vlNp1eGWO6hWvibC1CJAtZXRcCpgCAsRE6LzoDVbnADyrXoA8U6fNua5DLL3Beh7kPYA8L0FnpcDACgiQjsHVG6yO4u+sOiwgdK5kv2y9tK5Dlp2658t6+6o3WDkSG+tQgBgI/LniP7a+Wnv+JOPPhi3t8zzXmPEb3O0yLVLgmAIhALGO/gmGNREo3nnq/rR7WQSKiaflfQU6A/LaYevddO53wVgX7BUUveJdR8rZvmtAEhwMn0rJBigAV6q8s+W8kw3P1nV3v17x3klyd8AtIBXgyI33Bkn2S2uHCrgEpCIcOQTdIbE2ds0eJoR+Dt1EeLW4A8Q6DkOgksOqLQ2AhLnN1vnax3sDole8OyLsDhDzuQwcVtCOhbdjxuS1d2fd4aou6aGAK0Csp5I9eujAZyet69YbrB6RfrNOV4CY7z07DUOulpB0zVp6g2W9bcfiBhDLHnQYbIfb1S5pDUm1hqjgMgq4AcSa0xsOc+FzAZKqk/f0MPisrNN/eoO1U3hPgOx0ucllTyfvBTzo/TDQG6ydQfQESM/5x5ZcxSbvZI1kZKeWb+q22qbjOxTwBMgOd7tcwpDr8OR9AQVgYL2zRxcRo1YqQPZF7m1IVmB4g0JzkH1xvwiQnULZZbsgMTD44a2rXe82W+gVr0Vn5yJAdgqVLgMSOn7a/VoZFEzqma8Axpn5xVeB+t+NAp4A+e1GleeOAIMxcWWNAQzmbRi11QoNr7aUeXDcEyAP3HN9CCiwKGBkMfWKNyuxY+0GkDQu1tNtR9B0STsF3ACSmhxlmJXcDbny+HmTWyFdAZKyiFuxYjv27T3zpl/fe9p4qoArQJKnP9Jaq3oK8DYu2typnhpPSnYHSMoimos8CVqhU3odvUNId4Dgs0FCFhEkiFHPGGopi7zQ1yUg+CxIUKG68Zq6eiWRK3ALCKIKElSoa/aJ53lI6rrYtXTXgKBMgkSvfxGjjmmo9URX94Dgu0HCa0lBghh1TBP2DV1DAILvggQVqhlZhIdQtQqiFhwGEAQWJKhQzfTZyANpQwGC/wkSXgOzKyurgLuhVtnmvV9aOEBookHyt9mHbeuzEhOh4MJQS5+NLAQNCUj23yAhk2jyngUps1YWWegYGhDaYZAwuRQkiFHGyCJoWqa04KWEBwT9BQkqFDVN2JOcQwBCWxIkDLnYlZ1XYPSh1i6FhgGE1hokTNqBhDWHZMcVYKg1/YR9KEDoC0BiJkgQ47xN/z2t4QDJfSJBosl7FuTg+nq9Tj1hHxYQ+oNBQnAFCWIct6kn7EMDQp8QJKhw2qadsB8D5LTebQtIkDAvaVvxOLUxYScbj9OinS2ZAhC0MEgifD2Ft2+AjOG2J5syi0wDSO5pBgqdz9O8BCh+m18sP+w/QOYYlt12sbYJ+3RvtaYDhJ5mnZDhQm9IAAAgMPzBtaX19m/pS95mqDXVZyNTAkK0O0KyBINt3Lkz849zHiGZaqjlDpC7nlLxgHVCntytOiEdnmyBsf2yZcm/Xde+LKzcBWQRdCtXouOSpgaEuNAJzfjZkhqg0Llv5hfU+abV8OtNF+4un+azkekByaE3SH6ZlQIFMMgU2KmnrflEWVh21ct6iqGWAFl1N+uQS1DeeXrTiTGgwNhelX5s13zizduxm+vdxVBr+Am7ANnoQNYpAQUjq9BBgQWj4y+NcwCRjXMbpZ46TN2nCqhw8/BZZCZADvcPg4XPJoAFyyDkNedqQfHts/nAUK16Pd8V7tsgi+DXvqsDXiVAYgXNZRaxDxCHHWoJkECAWBYhg7iEJJCMb7kqQN6Sq//FBonHIQ1DrSGziADp3+ePeMCLgSP31bxnyO9pCZAiXaZtIZZFGGphbSt+UZvNRTxmtxdePz8tQJ7r4/msy7mIQTLUUEuAeEbgiW8pi7iE5Inb4U4JkHAh+89hg4QhjbehFhN2/PrP0cBbAiRw8JLryiJJiBorAVJD1ZJlvijLsggZBHtxZdvTNhcZ4q2WAGnbb2rV5jGLMNQKP2EXILW6bMNyUxbxCEn4LzMKkIYduWZVBgkTY29DrfBZRIDU7LXty1YWKay5ACksaM/iLIuQQbBdbjS6KHQWESCNekmragwSj9/TatX84vUIkOKSuijQGyRhJ+sCxEV/LuuEZRGGWVjZgicsTYCMG3SPE/ZwaguQcCHb53DKIr0gWTsZ9gNDAbIO5UD7BomXz0bCDvcEyEBAbDTFSxbZcM/3YQHiOz6nvbMswtMbO13WiQL+OXFv11sFSFf5m1WuLHJQagFyULhIt6Us0g0Sq5+5UCHJ2hYjQNrq3a221El7DLW6gVlCbAFSQsU4ZTTvrAnMOAqtPBUgK0FG3rXOSgbBWjWzOZClGyZASivqvDyDhO9ptYCEX+oddu6RwyhAshJzrVs82VvUUTZqD0oTIA9EGf2QZREyCJmkVlP50xDUUav8ZuUKkGZS+6qoEiRAMQwcREyAoMKkBiRm/AWtEsMh5hxDwUG3ECCoMLkZJPzlrKOg5KxRc8jWLUICpJv0/ipegUJWofOvHeUYxnkyBsb++roh9ksBMoQYasSXAgkUsgqd33ZvFo5hnB8WjC8lLhcBkpXQWgo8UECAPBBFh6RAVkCAZCW0lgIPFBAgD0TRISmQFQgASHZVaynQXgEB0l5z1RhIAQESKFhytb0CAqS95qoxkAICJFCw5Gp7BeYGpL3eqjGYAgIkWMDkblsFBEhbvVVbMAUESLCAyd22CgiQtnqrtmAKCJBKAVOxYyjwLwAAAP//SHVxOQAAAAZJREFUAwC7wCXN4JhaXwAAAABJRU5ErkJggg==";
14
17
 
18
+ // assets/imgs/vehicle.png
19
+ var vehicle_default = "data:image/png;base64,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";
20
+
15
21
  // assets/imgs/view.png
16
22
  var view_default = "data:image/png;base64,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";
17
23
 
@@ -291,6 +297,7 @@ var PlayerController = class {
291
297
  this.loader = new GLTFLoader();
292
298
  this.controllerMode = 0;
293
299
  // 0: 人物 1: 车辆
300
+ this.enableOverShoulderView = false;
294
301
  this.isChangeControllerTransitionTimer = null;
295
302
  // ==================== 玩家基本属性 ====================
296
303
  this.playerRadius = 45;
@@ -324,7 +331,7 @@ var PlayerController = class {
324
331
  // 全局车辆共享参数
325
332
  this.vehicleParams = {
326
333
  debug: {
327
- showPhysicsBox: false
334
+ showPhysicsBox: true
328
335
  },
329
336
  chassis: {
330
337
  linearDamping: 0.5,
@@ -345,7 +352,8 @@ var PlayerController = class {
345
352
  maxSteerAngle: Math.PI / 4,
346
353
  steerSpeed: 0.5,
347
354
  steerReturnSpeed: 1
348
- }
355
+ },
356
+ followVehicleDirection: true
349
357
  };
350
358
  this.camBehindDir = new THREE3.Vector3(0, 0, 1);
351
359
  // ==================== 上车相关 ====================
@@ -355,10 +363,16 @@ var PlayerController = class {
355
363
  this.boardingTargetDir = null;
356
364
  this.boardingMoveSpeed = 300;
357
365
  this.boardingRotateSpeed = 10;
358
- this.flip180Quat = new THREE3.Quaternion().setFromAxisAngle(new THREE3.Vector3(0, 1, 0), Math.PI);
359
- this.enterVehicleTimer = null;
366
+ this.flip180Quat = new THREE3.Quaternion().setFromAxisAngle(
367
+ new THREE3.Vector3(0, 1, 0),
368
+ Math.PI
369
+ );
360
370
  this.closeVehicleDoorTimer = null;
361
371
  this.boardingPointWorld = null;
372
+ this.isBoardingAnimPlaying = false;
373
+ this.closeDoorTriggered = false;
374
+ this.isExitAnimPlaying = false;
375
+ this.closeExitDoorTriggered = false;
362
376
  // ==================== 状态开关 ====================
363
377
  this.playerIsOnGround = false;
364
378
  this.isupdate = true;
@@ -380,15 +394,19 @@ var PlayerController = class {
380
394
  this.jumpBtnEl = null;
381
395
  this.flyBtnEl = null;
382
396
  this.viewBtnEl = null;
397
+ this.vehicleBtnEl = null;
383
398
  this.lookPointerId = null;
384
399
  this.isLookDown = false;
385
400
  this.lastTouchX = 0;
386
401
  this.lastTouchY = 0;
402
+ this.nearCheckLocal = new THREE3.Vector3();
403
+ this.nearCheckWorld = new THREE3.Vector3();
404
+ this.isNearVehicle = false;
387
405
  // ==================== 第三人称相机参数 ====================
388
406
  this._camCollisionLerp = 0.18;
389
407
  this._camEpsilon = 0.35;
390
- this._minCamDistance = 1;
391
- this._maxCamDistance = 4.4;
408
+ this.minCamDistance = 1;
409
+ this.maxCamDistance = 4.4;
392
410
  this.orginMaxCamDistance = 4.4;
393
411
  // ==================== 物理/运动 ====================
394
412
  this.playerVelocity = new THREE3.Vector3();
@@ -414,8 +432,16 @@ var PlayerController = class {
414
432
  // ==================== 射线检测 ====================
415
433
  this._personToCam = new THREE3.Vector3();
416
434
  this._originTmp = new THREE3.Vector3();
417
- this._raycaster = new THREE3.Raycaster(new THREE3.Vector3(), new THREE3.Vector3(0, -1, 0));
418
- this._raycasterPersonToCam = new THREE3.Raycaster(new THREE3.Vector3(), new THREE3.Vector3());
435
+ this._raycaster = new THREE3.Raycaster(
436
+ new THREE3.Vector3(),
437
+ new THREE3.Vector3(0, -1, 0)
438
+ );
439
+ this._raycasterPersonToCam = new THREE3.Raycaster(
440
+ new THREE3.Vector3(),
441
+ new THREE3.Vector3()
442
+ );
443
+ this.centerRay = new THREE3.Raycaster();
444
+ this.centerMouse = new THREE3.Vector2();
419
445
  // ==================== 物理与碰撞检测 ====================
420
446
  this.ensureAttributesMinimal = (geom) => {
421
447
  if (!geom.attributes.position) return null;
@@ -428,16 +454,20 @@ var PlayerController = class {
428
454
  return geom;
429
455
  };
430
456
  this.setAnimationByPressed = () => {
431
- this._maxCamDistance = this.orginMaxCamDistance;
457
+ this.maxCamDistance = this.orginMaxCamDistance;
432
458
  if (this.isMovingToBoardingPoint) {
433
459
  this.isMovingToBoardingPoint = false;
434
460
  this.boardingWaypoints = [];
435
461
  this.currentWaypointIndex = 0;
436
462
  this.boardingTargetDir = null;
437
463
  }
438
- if (this.enterVehicleTimer) {
439
- clearTimeout(this.enterVehicleTimer);
440
- this.enterVehicleTimer = null;
464
+ if (this.isExitAnimPlaying) {
465
+ this.isExitAnimPlaying = false;
466
+ this.closeExitDoorTriggered = false;
467
+ }
468
+ if (this.isBoardingAnimPlaying) {
469
+ this.isBoardingAnimPlaying = false;
470
+ this.closeDoorTriggered = false;
441
471
  }
442
472
  if (this.closeVehicleDoorTimer) {
443
473
  clearTimeout(this.closeVehicleDoorTimer);
@@ -449,7 +479,7 @@ var PlayerController = class {
449
479
  return;
450
480
  }
451
481
  this.playPersonAnimationByName("flying");
452
- this._maxCamDistance = this.orginMaxCamDistance * 2;
482
+ this.maxCamDistance = this.orginMaxCamDistance * 2;
453
483
  return;
454
484
  }
455
485
  if (this.playerIsOnGround) {
@@ -458,11 +488,15 @@ var PlayerController = class {
458
488
  return;
459
489
  }
460
490
  if (this.fwdPressed) {
461
- this.playPersonAnimationByName(this.shiftPressed ? "running" : "walking");
491
+ this.playPersonAnimationByName(
492
+ this.shiftPressed ? "running" : "walking"
493
+ );
462
494
  return;
463
495
  }
464
496
  if (!this.isFirstPerson && (this.lftPressed || this.rgtPressed || this.bkdPressed)) {
465
- this.playPersonAnimationByName(this.shiftPressed ? "running" : "walking");
497
+ this.playPersonAnimationByName(
498
+ this.shiftPressed ? "running" : "walking"
499
+ );
466
500
  return;
467
501
  }
468
502
  if (this.lftPressed) {
@@ -519,7 +553,14 @@ var PlayerController = class {
519
553
  this.controls.mouseButtons = { LEFT: 2, MIDDLE: 1, RIGHT: 0 };
520
554
  break;
521
555
  case "Space":
556
+ if (this.isMovingToBoardingPoint) {
557
+ this.isMovingToBoardingPoint = false;
558
+ this.boardingWaypoints = [];
559
+ this.currentWaypointIndex = 0;
560
+ this.boardingTargetDir = null;
561
+ }
522
562
  this.spacePressed = true;
563
+ if (this.controllerMode == 1) return;
523
564
  if (!this.playerIsOnGround || this.isFlying) return;
524
565
  const next = this.personActions?.get("jumping");
525
566
  if (next && this.actionState === next) return;
@@ -534,11 +575,12 @@ var PlayerController = class {
534
575
  this.changeView();
535
576
  break;
536
577
  case "KeyF":
537
- if (this.controllerMode == 1) return;
538
- this.isFlying = !this.isFlying;
539
- this.setAnimationByPressed();
540
- if (!this.isFlying && !this.playerIsOnGround) {
541
- this.playPersonAnimationByName("jumping");
578
+ if (this.controllerMode == 0 && this.playerFlyEnabled) {
579
+ this.isFlying = !this.isFlying;
580
+ this.setAnimationByPressed();
581
+ if (!this.isFlying && !this.playerIsOnGround) {
582
+ this.playPersonAnimationByName("jumping");
583
+ }
542
584
  }
543
585
  break;
544
586
  case "KeyE":
@@ -633,15 +675,17 @@ var PlayerController = class {
633
675
  this.mouseSensity = opts.mouseSensity ?? 5;
634
676
  const s = this.playerModel.scale;
635
677
  this.gravity = (opts.playerModel.gravity ?? -2400) * s;
636
- this.jumpHeight = (opts.playerModel.jumpHeight ?? 800) * s;
637
- this.originPlayerSpeed = (opts.playerModel.speed ?? 400) * s;
638
- this.playerSpeed = this.originPlayerSpeed;
678
+ this.jumpHeight = (opts.playerModel.jumpHeight ?? 600) * s;
679
+ this.playerSpeed = (opts.playerModel.speed ?? 300) * s;
680
+ this.playerFlySpeed = (opts.playerModel.playerFlySpeed ?? 2100) * s;
681
+ this.curPlayerSpeed = this.playerSpeed;
639
682
  this.playerModel.rotateY = opts.playerModel.rotateY ?? 0;
683
+ this.playerFlyEnabled = opts.playerModel.flyEnabled ?? true;
640
684
  this._camCollisionLerp = 0.18;
641
685
  this._camEpsilon = 35 * s;
642
- this._minCamDistance = (opts.minCamDistance ?? 100) * s;
643
- this._maxCamDistance = (opts.maxCamDistance ?? 440) * s;
644
- this.orginMaxCamDistance = this._maxCamDistance;
686
+ this.minCamDistance = (opts.minCamDistance ?? 100) * s;
687
+ this.maxCamDistance = (opts.maxCamDistance ?? 440) * s;
688
+ this.orginMaxCamDistance = this.maxCamDistance;
645
689
  this.thirdMouseMode = opts.thirdMouseMode ?? 1;
646
690
  this.enableZoom = opts.enableZoom ?? false;
647
691
  const isMobileDevice = () => navigator.maxTouchPoints && navigator.maxTouchPoints > 0 || "ontouchstart" in window || /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
@@ -655,6 +699,24 @@ var PlayerController = class {
655
699
  this.setCameraPos();
656
700
  this.setControls();
657
701
  if (callback) callback();
702
+ this.enableOverShoulderView = opts.enableOverShoulderView ?? false;
703
+ this.setOverShoulderView(this.enableOverShoulderView);
704
+ }
705
+ setOverShoulderView(enable) {
706
+ if (!enable || this.controllerMode == 1) {
707
+ this.camera.clearViewOffset();
708
+ return;
709
+ }
710
+ const w = window.innerWidth;
711
+ const h = window.innerHeight;
712
+ this.camera.setViewOffset(
713
+ w,
714
+ h,
715
+ -w * -0.15,
716
+ 0,
717
+ w,
718
+ h
719
+ );
658
720
  }
659
721
  async initLoader() {
660
722
  const dracoLoader = new DRACOLoader();
@@ -664,7 +726,7 @@ var PlayerController = class {
664
726
  }
665
727
  async initRapier() {
666
728
  if (this.RAPIER) return;
667
- this.RAPIER = await import("./rapier.es-XQHNYU2P.mjs");
729
+ this.RAPIER = await import("@dimforge/rapier3d-compat");
668
730
  await this.RAPIER.init();
669
731
  const gravity = new this.RAPIER.Vector3(0, -9.81, 0);
670
732
  this.world = new this.RAPIER.World(gravity);
@@ -701,7 +763,9 @@ var PlayerController = class {
701
763
  // ==================== 玩家模型相关方法 ====================
702
764
  async loadPersonGLB() {
703
765
  try {
704
- const gltf = await this.loader.loadAsync(this.playerModel.url);
766
+ const gltf = await this.loader.loadAsync(
767
+ this.playerModel.url
768
+ );
705
769
  this.person = gltf.scene;
706
770
  const { size } = this.getBbox(this.person);
707
771
  const ratio = this.playerHeight / size.y;
@@ -721,11 +785,17 @@ var PlayerController = class {
721
785
  });
722
786
  const r = this.playerRadius * scale;
723
787
  const h = this.playerHeight * scale;
724
- this.player = new THREE3.Mesh(new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75), material);
788
+ this.player = new THREE3.Mesh(
789
+ new RoundedBoxGeometry(r * 2, h, r * 2, 1, 75),
790
+ material
791
+ );
725
792
  this.player.geometry.translate(0, -h * 0.25, 0);
726
793
  this.player.capsuleInfo = {
727
794
  radius: r,
728
- segment: new THREE3.Line3(new THREE3.Vector3(), new THREE3.Vector3(0, -h * 0.5, 0))
795
+ segment: new THREE3.Line3(
796
+ new THREE3.Vector3(),
797
+ new THREE3.Vector3(0, -h * 0.5, 0)
798
+ )
729
799
  };
730
800
  this.player.name = "capsule";
731
801
  this.scene.add(this.player);
@@ -742,6 +812,7 @@ var PlayerController = class {
742
812
  this.reset();
743
813
  this.personMixer = new THREE3.AnimationMixer(this.person);
744
814
  const animations = gltf.animations ?? [];
815
+ console.log("animations", animations);
745
816
  this.personActions = /* @__PURE__ */ new Map();
746
817
  const animationMappings = [
747
818
  [this.playerModel.idleAnim, "idle"],
@@ -790,7 +861,9 @@ var PlayerController = class {
790
861
  const finishedAction = ev.action;
791
862
  if (finishedAction === this.jumpAction) {
792
863
  if (this.fwdPressed) {
793
- this.playPersonAnimationByName(this.shiftPressed ? "running" : "walking");
864
+ this.playPersonAnimationByName(
865
+ this.shiftPressed ? "running" : "walking"
866
+ );
794
867
  return;
795
868
  }
796
869
  if (this.bkdPressed) {
@@ -803,11 +876,56 @@ var PlayerController = class {
803
876
  }
804
877
  this.playPersonAnimationByName("idle");
805
878
  }
879
+ if (finishedAction === this.personActions?.get("enterCar")) {
880
+ this.onEnterCarAnimFinished();
881
+ }
882
+ if (finishedAction === this.personActions?.get("exitCar")) {
883
+ }
806
884
  });
807
885
  } catch (error) {
808
886
  console.error("\u52A0\u8F7D\u73A9\u5BB6\u6A21\u578B\u5931\u8D25:", error);
809
887
  }
810
888
  }
889
+ async switchPlayerModel(newPlayerModel) {
890
+ const savedPos = this.player.position.clone();
891
+ const savedQuat = this.player.quaternion.clone();
892
+ const wasFirstPerson = this.isFirstPerson;
893
+ if (wasFirstPerson) {
894
+ this.scene.attach(this.camera);
895
+ }
896
+ if (this.player) {
897
+ this.scene.remove(this.player);
898
+ }
899
+ if (this.person) {
900
+ this.player.remove(this.person);
901
+ this.person = null;
902
+ this.personHead = null;
903
+ }
904
+ if (this.personMixer) {
905
+ this.personMixer.stopAllAction();
906
+ this.personMixer.uncacheRoot(this.personMixer.getRoot());
907
+ this.personMixer = void 0;
908
+ this.personActions = void 0;
909
+ }
910
+ const ratio = newPlayerModel.scale / this.playerModel.scale;
911
+ this.playerModel = { ...this.playerModel, ...newPlayerModel };
912
+ this.gravity *= ratio;
913
+ this.jumpHeight *= ratio;
914
+ this.playerSpeed *= ratio;
915
+ this.playerFlySpeed *= ratio;
916
+ this.curPlayerSpeed *= ratio;
917
+ this._camEpsilon *= ratio;
918
+ this.minCamDistance *= ratio;
919
+ this.maxCamDistance *= ratio;
920
+ this.orginMaxCamDistance *= ratio;
921
+ await this.loadPersonGLB();
922
+ this.player.position.copy(savedPos);
923
+ this.player.quaternion.copy(savedQuat);
924
+ if (wasFirstPerson) {
925
+ this.setFirstPersonCamera();
926
+ }
927
+ this.setDebug(this.displayCollider);
928
+ }
811
929
  playPersonAnimationByName(name, fade = 0.18) {
812
930
  if (!this.personActions || this.ctPressed) return;
813
931
  const next = this.personActions.get(name);
@@ -845,9 +963,12 @@ var PlayerController = class {
845
963
  const scale = opts.scale ?? 1;
846
964
  const chassisRatio = opts.chassisRatio ?? 0.2;
847
965
  const suspensionRestLengthRatio = opts.suspensionRestLengthRatio ?? 0.2;
966
+ const speedMultiplier = opts.speedMultiplier ?? 1;
967
+ const followVehicleDirection = opts.followVehicleDirection ?? true;
848
968
  this.vehicleParams.power.accelerateForce = 50 * scale;
849
969
  this.vehicleParams.power.brakeForce = 200 * scale;
850
970
  this.vehicleParams.power.maxSpeed = 1e4 * scale;
971
+ this.vehicleParams.followVehicleDirection = followVehicleDirection;
851
972
  const vehicleModel = await this.loader.loadAsync(opts.url);
852
973
  const { size: originalSize } = this.getBbox(vehicleModel.scene);
853
974
  const ratio = this.vehicleLength / Math.max(originalSize.x, originalSize.y, originalSize.z);
@@ -946,13 +1067,25 @@ var PlayerController = class {
946
1067
  vehicleModel.scene.position.y -= chassisHeight / 2;
947
1068
  halfExtents.x *= 0.95;
948
1069
  halfExtents.z *= 0.95;
949
- const chassisDesc = this.RAPIER.RigidBodyDesc.dynamic().setTranslation(opts.position.x, opts.position.y, opts.position.z).setLinearDamping(this.vehicleParams.chassis.linearDamping).setAngularDamping(this.vehicleParams.chassis.angularDamping).setCanSleep(false).setAdditionalMass(10);
1070
+ const chassisDesc = this.RAPIER.RigidBodyDesc.dynamic().setTranslation(
1071
+ opts.position.x,
1072
+ opts.position.y,
1073
+ opts.position.z
1074
+ ).setLinearDamping(this.vehicleParams.chassis.linearDamping).setAngularDamping(this.vehicleParams.chassis.angularDamping).setCanSleep(true).setAdditionalMass(10);
950
1075
  const chassisBody = this.world.createRigidBody(chassisDesc);
951
- const chassisCollider = this.RAPIER.ColliderDesc.cuboid(halfExtents.x, halfExtents.y, halfExtents.z);
1076
+ const chassisCollider = this.RAPIER.ColliderDesc.cuboid(
1077
+ halfExtents.x,
1078
+ halfExtents.y,
1079
+ halfExtents.z
1080
+ );
952
1081
  this.world.createCollider(chassisCollider, chassisBody);
953
1082
  if (this.vehicleParams.debug.showPhysicsBox) {
954
1083
  const debugBox = new THREE3.Mesh(
955
- new THREE3.BoxGeometry(halfExtents.x * 2, halfExtents.y * 2, halfExtents.z * 2),
1084
+ new THREE3.BoxGeometry(
1085
+ halfExtents.x * 2,
1086
+ halfExtents.y * 2,
1087
+ halfExtents.z * 2
1088
+ ),
956
1089
  new THREE3.MeshBasicMaterial({
957
1090
  color: 16711680,
958
1091
  wireframe: true,
@@ -964,7 +1097,12 @@ var PlayerController = class {
964
1097
  }
965
1098
  vehicleGroup.position.copy(opts.position);
966
1099
  vehicleGroup.updateMatrixWorld(true);
967
- const { vehicle, updateWheelVisuals } = createVehicleController(this.world, chassisBody, wheelWrappers, wheelsInfo);
1100
+ const { vehicle, updateWheelVisuals } = createVehicleController(
1101
+ this.world,
1102
+ chassisBody,
1103
+ wheelWrappers,
1104
+ wheelsInfo
1105
+ );
968
1106
  const vehicleInstance = {
969
1107
  vehicleGroup,
970
1108
  chassisBody,
@@ -974,11 +1112,14 @@ var PlayerController = class {
974
1112
  vehicleActions,
975
1113
  vehiclIsOpenDoor: false,
976
1114
  vehicleBBox: bbox.clone(),
977
- pathPlanner: new PathPlanner(this._createObstacleCheckerFor(vehicleGroup, bbox, scale), {
978
- debugEnabled: false,
979
- scene: this.scene,
980
- scale: this.playerModel.scale
981
- }),
1115
+ pathPlanner: new PathPlanner(
1116
+ this._createObstacleCheckerFor(vehicleGroup, bbox, scale),
1117
+ {
1118
+ debugEnabled: false,
1119
+ scene: this.scene,
1120
+ scale: this.playerModel.scale
1121
+ }
1122
+ ),
982
1123
  scale,
983
1124
  boardingPoint: opts.boardingPoint,
984
1125
  seatOffset: opts.seatOffset ?? new THREE3.Vector3(0, 0, 0),
@@ -989,10 +1130,10 @@ var PlayerController = class {
989
1130
  l: Math.max(size.x, size.z),
990
1131
  w: Math.min(size.x, size.z),
991
1132
  h: size.y
992
- }
1133
+ },
1134
+ speedMultiplier
993
1135
  };
994
1136
  this.vehicles.push(vehicleInstance);
995
- console.log(`\u8F66\u8F86\u52A0\u8F7D\u5B8C\u6210\uFF0C\u5F53\u524D\u5171 ${this.vehicles.length} \u8F86\u8F66`, vehicleInstance);
996
1137
  this.setControllerTransition();
997
1138
  } catch (error) {
998
1139
  console.error("\u52A0\u8F7D\u8F66\u8F86\u6A21\u578B\u5931\u8D25:", error);
@@ -1024,7 +1165,11 @@ var PlayerController = class {
1024
1165
  for (let x = -1; x <= 1; x += 2) {
1025
1166
  for (let y = -1; y <= 1; y += 2) {
1026
1167
  for (let z = -1; z <= 1; z += 2) {
1027
- const localCorner = new THREE3.Vector3(halfSize.x * x, halfSize.y * y, halfSize.z * z);
1168
+ const localCorner = new THREE3.Vector3(
1169
+ halfSize.x * x,
1170
+ halfSize.y * y,
1171
+ halfSize.z * z
1172
+ );
1028
1173
  const worldCorner = localCorner.applyQuaternion(vehicleQuat).add(center);
1029
1174
  corners.push(worldCorner);
1030
1175
  }
@@ -1045,8 +1190,14 @@ var PlayerController = class {
1045
1190
  const nodes = [];
1046
1191
  const vehiclePos = vehicleGroup.position;
1047
1192
  const vehicleQuat = vehicleGroup.quaternion;
1048
- const vehicleForward = new THREE3.Vector3(0, 0, 1).applyQuaternion(vehicleQuat);
1049
- const vehicleRight = new THREE3.Vector3(1, 0, 0).applyQuaternion(vehicleQuat);
1193
+ const vehicleForward = new THREE3.Vector3(
1194
+ 0,
1195
+ 0,
1196
+ 1
1197
+ ).applyQuaternion(vehicleQuat);
1198
+ const vehicleRight = new THREE3.Vector3(1, 0, 0).applyQuaternion(
1199
+ vehicleQuat
1200
+ );
1050
1201
  const bboxSize = new THREE3.Vector3();
1051
1202
  bbox.getSize(bboxSize);
1052
1203
  const halfLength = bboxSize.z / 2 * scale;
@@ -1056,16 +1207,32 @@ var PlayerController = class {
1056
1207
  const groundY = start.y;
1057
1208
  for (const margin of [bypassMargin, extendedMargin]) {
1058
1209
  nodes.push(
1059
- vehiclePos.clone().add(vehicleForward.clone().multiplyScalar(halfLength + margin)).add(vehicleRight.clone().multiplyScalar(-halfWidth - margin)).setY(groundY)
1210
+ vehiclePos.clone().add(
1211
+ vehicleForward.clone().multiplyScalar(halfLength + margin)
1212
+ ).add(
1213
+ vehicleRight.clone().multiplyScalar(-halfWidth - margin)
1214
+ ).setY(groundY)
1060
1215
  );
1061
1216
  nodes.push(
1062
- vehiclePos.clone().add(vehicleForward.clone().multiplyScalar(halfLength + margin)).add(vehicleRight.clone().multiplyScalar(halfWidth + margin)).setY(groundY)
1217
+ vehiclePos.clone().add(
1218
+ vehicleForward.clone().multiplyScalar(halfLength + margin)
1219
+ ).add(
1220
+ vehicleRight.clone().multiplyScalar(halfWidth + margin)
1221
+ ).setY(groundY)
1063
1222
  );
1064
1223
  nodes.push(
1065
- vehiclePos.clone().add(vehicleForward.clone().multiplyScalar(-halfLength - margin)).add(vehicleRight.clone().multiplyScalar(-halfWidth - margin)).setY(groundY)
1224
+ vehiclePos.clone().add(
1225
+ vehicleForward.clone().multiplyScalar(-halfLength - margin)
1226
+ ).add(
1227
+ vehicleRight.clone().multiplyScalar(-halfWidth - margin)
1228
+ ).setY(groundY)
1066
1229
  );
1067
1230
  nodes.push(
1068
- vehiclePos.clone().add(vehicleForward.clone().multiplyScalar(-halfLength - margin)).add(vehicleRight.clone().multiplyScalar(halfWidth + margin)).setY(groundY)
1231
+ vehiclePos.clone().add(
1232
+ vehicleForward.clone().multiplyScalar(-halfLength - margin)
1233
+ ).add(
1234
+ vehicleRight.clone().multiplyScalar(halfWidth + margin)
1235
+ ).setY(groundY)
1069
1236
  );
1070
1237
  }
1071
1238
  return nodes;
@@ -1100,14 +1267,16 @@ var PlayerController = class {
1100
1267
  * 上车:自动寻找最近的车辆
1101
1268
  */
1102
1269
  enterVehicle() {
1103
- if (this.vehicles.length === 0) return;
1270
+ if (this.vehicles.length === 0 || this.isMovingToBoardingPoint) return;
1104
1271
  let nearestVehicle = null;
1105
1272
  let nearestDist = Infinity;
1106
1273
  let nearBoardingPointWorld = null;
1107
1274
  for (const v2 of this.vehicles) {
1108
1275
  const boardingPointLocal = v2.boardingPoint.clone().multiplyScalar(v2.scale);
1109
1276
  const boardingPointWorld = new THREE3.Vector3();
1110
- v2.vehicleGroup.localToWorld(boardingPointWorld.copy(boardingPointLocal));
1277
+ v2.vehicleGroup.localToWorld(
1278
+ boardingPointWorld.copy(boardingPointLocal)
1279
+ );
1111
1280
  const dist = this.player.position.distanceTo(boardingPointWorld);
1112
1281
  if (dist < 800 * this.playerModel.scale && dist < nearestDist) {
1113
1282
  nearestDist = dist;
@@ -1118,9 +1287,15 @@ var PlayerController = class {
1118
1287
  if (!nearestVehicle || !nearBoardingPointWorld) return;
1119
1288
  this.activeVehicle = nearestVehicle;
1120
1289
  const v = nearestVehicle;
1290
+ const vel = v.chassisBody.linvel();
1291
+ const horizSpeed = Math.sqrt(vel.x * vel.x + vel.z * vel.z);
1292
+ if (horizSpeed > 0.1) return;
1121
1293
  this.boardingPointWorld = nearBoardingPointWorld;
1122
1294
  const vehicleForward = new THREE3.Vector3(0, 0, 1).applyQuaternion(v.vehicleGroup.quaternion).normalize();
1123
- const path = v.pathPlanner.findPath(this.player.position.clone(), this.boardingPointWorld);
1295
+ const path = v.pathPlanner.findPath(
1296
+ this.player.position.clone(),
1297
+ this.boardingPointWorld
1298
+ );
1124
1299
  this.boardingWaypoints = path;
1125
1300
  this.currentWaypointIndex = 0;
1126
1301
  this.boardingTargetDir = vehicleForward;
@@ -1140,15 +1315,29 @@ var PlayerController = class {
1140
1315
  }
1141
1316
  const currentWaypoint = this.boardingWaypoints[this.currentWaypointIndex];
1142
1317
  const currentPos = this.player.position.clone();
1143
- const horizontalDistance = new THREE3.Vector2(currentWaypoint.x - currentPos.x, currentWaypoint.z - currentPos.z).length();
1318
+ const horizontalDistance = new THREE3.Vector2(
1319
+ currentWaypoint.x - currentPos.x,
1320
+ currentWaypoint.z - currentPos.z
1321
+ ).length();
1144
1322
  const isLastWaypoint = this.currentWaypointIndex === this.boardingWaypoints.length - 1;
1145
1323
  const waypointThreshold = isLastWaypoint ? 0 : 10 * this.playerModel.scale;
1146
1324
  if (horizontalDistance > waypointThreshold) {
1147
- const moveDir = new THREE3.Vector3(currentWaypoint.x - currentPos.x, 0, currentWaypoint.z - currentPos.z).normalize();
1148
- const moveDistance = Math.min(this.boardingMoveSpeed * this.playerModel.scale * delta, horizontalDistance);
1325
+ const moveDir = new THREE3.Vector3(
1326
+ currentWaypoint.x - currentPos.x,
1327
+ 0,
1328
+ currentWaypoint.z - currentPos.z
1329
+ ).normalize();
1330
+ const moveDistance = Math.min(
1331
+ this.boardingMoveSpeed * this.playerModel.scale * delta,
1332
+ horizontalDistance
1333
+ );
1149
1334
  this.player.position.add(moveDir.multiplyScalar(moveDistance));
1150
1335
  const lookTarget = this.player.position.clone().add(moveDir);
1151
- this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);
1336
+ this.targetMat.lookAt(
1337
+ this.player.position,
1338
+ lookTarget,
1339
+ this.player.up
1340
+ );
1152
1341
  this.targetQuat.setFromRotationMatrix(this.targetMat);
1153
1342
  this.targetQuat.multiply(this.flip180Quat);
1154
1343
  const rotateAlpha = Math.min(1, this.boardingRotateSpeed * delta);
@@ -1162,13 +1351,17 @@ var PlayerController = class {
1162
1351
  */
1163
1352
  finalizeBoarding(delta) {
1164
1353
  const v = this.activeVehicle;
1165
- if (!this.boardingTargetDir || !v) return;
1354
+ if (!this.boardingTargetDir || !v || !this.isMovingToBoardingPoint) return;
1166
1355
  const currentDir = new THREE3.Vector3(0, 0, -1).applyQuaternion(this.player.quaternion).normalize();
1167
1356
  const targetDir = this.boardingTargetDir.clone().normalize();
1168
1357
  const angleDiff = currentDir.angleTo(targetDir);
1169
1358
  if (angleDiff > 0.01) {
1170
1359
  const lookTarget = this.player.position.clone().add(targetDir);
1171
- this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);
1360
+ this.targetMat.lookAt(
1361
+ this.player.position,
1362
+ lookTarget,
1363
+ this.player.up
1364
+ );
1172
1365
  this.targetQuat.setFromRotationMatrix(this.targetMat);
1173
1366
  const rotateAlpha = Math.min(1, this.boardingRotateSpeed * delta);
1174
1367
  this.player.quaternion.slerp(this.targetQuat, rotateAlpha);
@@ -1178,32 +1371,27 @@ var PlayerController = class {
1178
1371
  this.boardingTargetDir = null;
1179
1372
  v.pathPlanner?.clearVisualization();
1180
1373
  this.playPersonAnimationByName("enterCar");
1374
+ this.isBoardingAnimPlaying = true;
1375
+ this.closeDoorTriggered = false;
1181
1376
  if (!v.vehiclIsOpenDoor) this.openVehicleDoor();
1182
1377
  this.player.rotation.copy(v.vehicleGroup.rotation);
1183
1378
  this.player.quaternion.multiply(this.flip180Quat);
1184
- this.closeVehicleDoorTimer = setTimeout(
1185
- () => {
1186
- this.openVehicleDoor(false);
1187
- },
1188
- v.enterVehicleTime * 1e3 - 500
1189
- );
1190
- this.enterVehicleTimer = setTimeout(() => {
1191
- if (this.boardingPointWorld) {
1192
- const offsetY = this.boardingPointWorld.y - this.player.position.y;
1193
- this.controllerMode = 1;
1194
- v.vehicleGroup.attach(this.player);
1195
- this.player.position.add(
1196
- v.seatOffset.clone().multiplyScalar(v.scale).add(new THREE3.Vector3(0, offsetY, 0))
1197
- );
1198
- this.isMovingToBoardingPoint = false;
1199
- if (this.isChangeControllerTransitionTimer) {
1200
- clearTimeout(this.isChangeControllerTransitionTimer);
1201
- this.isChangeControllerTransitionTimer = null;
1202
- }
1203
- }
1204
- }, v.enterVehicleTime * 1e3);
1205
1379
  }
1206
1380
  }
1381
+ onEnterCarAnimFinished() {
1382
+ const v = this.activeVehicle;
1383
+ if (!v || !this.isMovingToBoardingPoint) return;
1384
+ this.player.updateMatrixWorld(true);
1385
+ const offsetY = this.boardingPointWorld.y - this.player.position.y;
1386
+ this.controllerMode = 1;
1387
+ this.syncControllerModeBtnEl();
1388
+ this.setOverShoulderView(false);
1389
+ v.vehicleGroup.attach(this.player);
1390
+ this.player.position.add(
1391
+ v.seatOffset.clone().multiplyScalar(v.scale).add(new THREE3.Vector3(0, offsetY, 0))
1392
+ );
1393
+ this.isMovingToBoardingPoint = false;
1394
+ }
1207
1395
  /**
1208
1396
  * 下车
1209
1397
  */
@@ -1215,20 +1403,19 @@ var PlayerController = class {
1215
1403
  this.currentWaypointIndex = 0;
1216
1404
  this.boardingTargetDir = null;
1217
1405
  const vel = v.chassisBody.linvel();
1218
- const velSpeed = new THREE3.Vector3(vel.x, vel.y, vel.z).length();
1219
- if (velSpeed > 0.1) {
1220
- this.playPersonAnimationByName("idle");
1221
- } else {
1406
+ const horizSpeed = Math.sqrt(vel.x * vel.x + vel.z * vel.z);
1407
+ const isStationary = horizSpeed < 0.1;
1408
+ if (isStationary) {
1222
1409
  this.playPersonAnimationByName("exitCar");
1410
+ this.isExitAnimPlaying = true;
1411
+ this.closeExitDoorTriggered = false;
1412
+ } else {
1413
+ this.playPersonAnimationByName("idle");
1223
1414
  }
1224
1415
  this.openVehicleDoor(true);
1225
- this.closeVehicleDoorTimer = setTimeout(
1226
- () => {
1227
- this.openVehicleDoor(false);
1228
- },
1229
- v.enterVehicleTime * 1e3 - 500
1230
- );
1231
1416
  this.controllerMode = 0;
1417
+ this.syncControllerModeBtnEl();
1418
+ this.setOverShoulderView(this.enableOverShoulderView);
1232
1419
  this.scene.attach(this.player);
1233
1420
  if (this.isFirstPerson) {
1234
1421
  this.setFirstPersonCamera();
@@ -1240,25 +1427,39 @@ var PlayerController = class {
1240
1427
  this.isFirstPerson = !this.isFirstPerson;
1241
1428
  if (this.isFirstPerson) {
1242
1429
  this.setFirstPersonCamera();
1430
+ this.setOverShoulderView(false);
1243
1431
  } else {
1432
+ this.controls.enabled = true;
1244
1433
  this.scene.attach(this.camera);
1245
1434
  const worldPos = this.player.position.clone();
1246
- const dir = new THREE3.Vector3(0, 0, -1).applyQuaternion(this.player.quaternion);
1435
+ const dir = new THREE3.Vector3(0, 0, -1).applyQuaternion(
1436
+ this.player.quaternion
1437
+ );
1247
1438
  const angle = Math.atan2(dir.z, dir.x);
1248
- const offset = new THREE3.Vector3(Math.cos(angle) * 400 * this.playerModel.scale, 200 * this.playerModel.scale, Math.sin(angle) * 400 * this.playerModel.scale);
1439
+ const offset = new THREE3.Vector3(
1440
+ Math.cos(angle) * 400 * this.playerModel.scale,
1441
+ 200 * this.playerModel.scale,
1442
+ Math.sin(angle) * 400 * this.playerModel.scale
1443
+ );
1249
1444
  this.camera.position.copy(worldPos).add(offset);
1250
1445
  this.controls.target.copy(worldPos);
1251
1446
  this.controls.enableZoom = this.enableZoom;
1447
+ this.setOverShoulderView(this.enableOverShoulderView);
1252
1448
  }
1253
1449
  this.setPointerLock();
1254
1450
  }
1255
1451
  setFirstPersonCamera() {
1452
+ this.controls.enabled = false;
1256
1453
  if (this.personHead) {
1257
1454
  this.personHead?.attach(this.camera);
1258
1455
  this.camera.position.set(0, 10, 20);
1259
1456
  } else {
1260
1457
  this.player.attach(this.camera);
1261
- this.camera.position.set(0, 40 * this.playerModel.scale, 30 * this.playerModel.scale);
1458
+ this.camera.position.set(
1459
+ 0,
1460
+ 40 * this.playerModel.scale,
1461
+ 30 * this.playerModel.scale
1462
+ );
1262
1463
  }
1263
1464
  this.camera.rotation.set(0, Math.PI, 0);
1264
1465
  this.controls.enableZoom = false;
@@ -1271,20 +1472,30 @@ var PlayerController = class {
1271
1472
  }
1272
1473
  }
1273
1474
  setCameraPos() {
1274
- setTimeout(() => {
1475
+ requestAnimationFrame(() => {
1275
1476
  if (this.isFirstPerson) {
1276
1477
  this.person.add(this.camera);
1277
- this.camera.position.set(0, 40 * this.playerModel.scale, 30 * this.playerModel.scale);
1478
+ this.camera.position.set(
1479
+ 0,
1480
+ 40 * this.playerModel.scale,
1481
+ 30 * this.playerModel.scale
1482
+ );
1278
1483
  } else {
1279
1484
  const worldPos = this.player.position.clone();
1280
- const dir = new THREE3.Vector3(0, 0, -1).applyQuaternion(this.player.quaternion);
1485
+ const dir = new THREE3.Vector3(0, 0, -1).applyQuaternion(
1486
+ this.player.quaternion
1487
+ );
1281
1488
  const angle = Math.atan2(dir.z, dir.x);
1282
- const offset = new THREE3.Vector3(Math.cos(angle) * 400 * this.playerModel.scale, -100 * this.playerModel.scale, Math.sin(angle) * 400 * this.playerModel.scale);
1489
+ const offset = new THREE3.Vector3(
1490
+ Math.cos(angle) * 400 * this.playerModel.scale,
1491
+ 200 * this.playerModel.scale,
1492
+ Math.sin(angle) * 400 * this.playerModel.scale
1493
+ );
1283
1494
  this.camera.position.copy(worldPos).add(offset);
1284
1495
  this.controls.enableZoom = this.enableZoom;
1285
1496
  }
1286
1497
  this.camera.updateProjectionMatrix();
1287
- }, 0);
1498
+ });
1288
1499
  }
1289
1500
  setControls() {
1290
1501
  this.controls.enableZoom = this.enableZoom;
@@ -1308,7 +1519,11 @@ var PlayerController = class {
1308
1519
  const yaw = -dx * speed * this.mouseSensity;
1309
1520
  const pitch = -dy * speed * this.mouseSensity;
1310
1521
  this.player.rotateY(yaw);
1311
- this.camera.rotation.x = THREE3.MathUtils.clamp(this.camera.rotation.x + pitch, -1.1, 1.4);
1522
+ this.camera.rotation.x = THREE3.MathUtils.clamp(
1523
+ this.camera.rotation.x + pitch,
1524
+ -1.1,
1525
+ 1.4
1526
+ );
1312
1527
  } else {
1313
1528
  const sensitivity = this.mouseSensity;
1314
1529
  const deltaX = -dx * speed * sensitivity;
@@ -1324,7 +1539,11 @@ var PlayerController = class {
1324
1539
  const newX = distance * Math.sin(phi) * Math.sin(theta);
1325
1540
  const newY = distance * Math.cos(phi);
1326
1541
  const newZ = distance * Math.sin(phi) * Math.cos(theta);
1327
- this.camera.position.set(target.x + newX, target.y + newY, target.z + newZ);
1542
+ this.camera.position.set(
1543
+ target.x + newX,
1544
+ target.y + newY,
1545
+ target.z + newZ
1546
+ );
1328
1547
  this.camera.lookAt(target);
1329
1548
  }
1330
1549
  } else {
@@ -1333,8 +1552,16 @@ var PlayerController = class {
1333
1552
  if (this.isFirstPerson) {
1334
1553
  const yaw = -dx * speed * this.mouseSensity;
1335
1554
  const pitch = -dy * speed * this.mouseSensity;
1336
- this.camera.rotation.y = THREE3.MathUtils.clamp(this.camera.rotation.y + yaw, Math.PI * (3 / 4), Math.PI * (5 / 4));
1337
- this.camera.rotation.x = THREE3.MathUtils.clamp(this.camera.rotation.x + pitch, 0, Math.PI * (1 / 3));
1555
+ this.camera.rotation.y = THREE3.MathUtils.clamp(
1556
+ this.camera.rotation.y + yaw,
1557
+ Math.PI * (3 / 4),
1558
+ Math.PI * (5 / 4)
1559
+ );
1560
+ this.camera.rotation.x = THREE3.MathUtils.clamp(
1561
+ this.camera.rotation.x + pitch,
1562
+ 0,
1563
+ Math.PI * (1 / 3)
1564
+ );
1338
1565
  } else {
1339
1566
  const sensitivity = this.mouseSensity;
1340
1567
  const deltaX = -dx * speed * sensitivity;
@@ -1350,7 +1577,11 @@ var PlayerController = class {
1350
1577
  const newX = distance * Math.sin(phi) * Math.sin(theta);
1351
1578
  const newY = distance * Math.cos(phi);
1352
1579
  const newZ = distance * Math.sin(phi) * Math.cos(theta);
1353
- this.camera.position.set(target.x + newX, target.y + newY, target.z + newZ);
1580
+ this.camera.position.set(
1581
+ target.x + newX,
1582
+ target.y + newY,
1583
+ target.z + newZ
1584
+ );
1354
1585
  this.camera.lookAt(target);
1355
1586
  }
1356
1587
  }
@@ -1374,7 +1605,12 @@ var PlayerController = class {
1374
1605
  const itemSize = attr.itemSize;
1375
1606
  const normalized = attr.normalized;
1376
1607
  if (!attrMap.has(name)) {
1377
- attrMap.set(name, { itemSize, arrayCtor: ctor, examples: 1, normalized });
1608
+ attrMap.set(name, {
1609
+ itemSize,
1610
+ arrayCtor: ctor,
1611
+ examples: 1,
1612
+ normalized
1613
+ });
1378
1614
  } else {
1379
1615
  const m = attrMap.get(name);
1380
1616
  if (m.itemSize !== itemSize || m.arrayCtor !== ctor || m.normalized !== normalized) {
@@ -1401,7 +1637,14 @@ var PlayerController = class {
1401
1637
  const meta = attrMap.get(name);
1402
1638
  const len = count * meta.itemSize;
1403
1639
  const array = new meta.arrayCtor(len);
1404
- g.setAttribute(name, new THREE3.BufferAttribute(array, meta.itemSize, meta.normalized));
1640
+ g.setAttribute(
1641
+ name,
1642
+ new THREE3.BufferAttribute(
1643
+ array,
1644
+ meta.itemSize,
1645
+ meta.normalized
1646
+ )
1647
+ );
1405
1648
  }
1406
1649
  }
1407
1650
  }
@@ -1432,15 +1675,37 @@ var PlayerController = class {
1432
1675
  this.collected = this.unifiedAttribute(this.collected);
1433
1676
  } else {
1434
1677
  const gltf = await this.loader.loadAsync(meshUrl);
1435
- const mesh = gltf.scene.children[0];
1436
- mesh.name = "BVH\u52A0\u8F7D\u6A21\u578B";
1437
- let geom = mesh.geometry.clone();
1438
- geom.applyMatrix4(mesh.matrixWorld);
1439
- if (geom.index) geom = geom.toNonIndexed();
1440
- const safe = this.ensureAttributesMinimal(geom);
1441
- if (safe) this.collected.push(safe);
1442
- }
1443
- const merged = BufferGeometryUtils.mergeGeometries(this.collected, false);
1678
+ const obj = gltf.scene.children[0];
1679
+ if (obj && obj?.geometry) {
1680
+ const mesh = obj;
1681
+ let geom = mesh.geometry.clone();
1682
+ geom.applyMatrix4(mesh.matrixWorld);
1683
+ if (geom.index) geom = geom.toNonIndexed();
1684
+ const safe = this.ensureAttributesMinimal(geom);
1685
+ if (safe) this.collected.push(safe);
1686
+ } else {
1687
+ obj.traverse((c) => {
1688
+ const mesh = c;
1689
+ if (mesh?.isMesh && mesh.geometry && c.name !== "capsule") {
1690
+ try {
1691
+ let geom = mesh.geometry.clone();
1692
+ geom.applyMatrix4(mesh.matrixWorld);
1693
+ if (geom.index) geom = geom.toNonIndexed();
1694
+ const safe = this.ensureAttributesMinimal(geom);
1695
+ if (safe) this.collected.push(safe);
1696
+ } catch (e) {
1697
+ console.warn("\u5904\u7406\u7F51\u683C\u65F6\u51FA\u9519\uFF1A", mesh, e);
1698
+ }
1699
+ }
1700
+ });
1701
+ if (!this.collected.length) return;
1702
+ this.collected = this.unifiedAttribute(this.collected);
1703
+ }
1704
+ }
1705
+ const merged = BufferGeometryUtils.mergeGeometries(
1706
+ this.collected,
1707
+ false
1708
+ );
1444
1709
  if (!merged) {
1445
1710
  console.error("\u5408\u5E76\u51E0\u4F55\u5931\u8D25");
1446
1711
  return;
@@ -1487,7 +1752,10 @@ var PlayerController = class {
1487
1752
  });
1488
1753
  if (!this.dynamicCollected.length) return;
1489
1754
  this.dynamicCollected = this.unifiedAttribute(this.dynamicCollected);
1490
- const merged = BufferGeometryUtils.mergeGeometries(this.dynamicCollected, false);
1755
+ const merged = BufferGeometryUtils.mergeGeometries(
1756
+ this.dynamicCollected,
1757
+ false
1758
+ );
1491
1759
  if (!merged) {
1492
1760
  console.error("\u5408\u5E76\u51E0\u4F55\u5931\u8D25");
1493
1761
  return;
@@ -1507,7 +1775,9 @@ var PlayerController = class {
1507
1775
  getAngleWithYAxis(normal) {
1508
1776
  const yAxis = { x: 0, y: 1, z: 0 };
1509
1777
  const dotProduct = normal.x * yAxis.x + normal.y * yAxis.y + normal.z * yAxis.z;
1510
- const normalMagnitude = Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
1778
+ const normalMagnitude = Math.sqrt(
1779
+ normal.x * normal.x + normal.y * normal.y + normal.z * normal.z
1780
+ );
1511
1781
  const cosTheta = dotProduct / normalMagnitude;
1512
1782
  return Math.acos(cosTheta);
1513
1783
  }
@@ -1517,12 +1787,15 @@ var PlayerController = class {
1517
1787
  clearTimeout(this.isChangeControllerTransitionTimer);
1518
1788
  this.isChangeControllerTransitionTimer = null;
1519
1789
  }
1790
+ let vGroups = [];
1791
+ for (const v of this.vehicles) {
1792
+ vGroups.push(v.vehicleGroup);
1793
+ }
1794
+ this.createDynamicBVH(vGroups);
1520
1795
  this.isChangeControllerTransitionTimer = setTimeout(() => {
1521
1796
  this.isChangeControllerTransitionTimer = null;
1522
- let vGroups = [];
1523
1797
  for (const v of this.vehicles) {
1524
1798
  this.clearVehicleVelocity(v);
1525
- vGroups.push(v.vehicleGroup);
1526
1799
  }
1527
1800
  this.createDynamicBVH(vGroups);
1528
1801
  }, 3e3);
@@ -1551,12 +1824,13 @@ var PlayerController = class {
1551
1824
  // ==================== 循环更新 ====================
1552
1825
  async update(delta = clock.getDelta()) {
1553
1826
  if (!this.isupdate || !this.player || !this.collider) return;
1554
- delta = Math.min(delta, 1 / 30);
1827
+ delta = Math.min(delta, 1 / 40);
1555
1828
  if (this.controllerMode == 1) {
1556
1829
  this.updateVehicle(delta);
1557
1830
  } else {
1558
1831
  this.updatePlayer(delta);
1559
- if (this.isChangeControllerTransitionTimer) this.updateVehicleInertia(delta);
1832
+ if (this.isChangeControllerTransitionTimer)
1833
+ this.updateVehicleInertia(delta);
1560
1834
  }
1561
1835
  }
1562
1836
  /**
@@ -1567,14 +1841,19 @@ var PlayerController = class {
1567
1841
  if (!v || !this.world) return;
1568
1842
  const { vehicleController, chassisBody, vehicleGroup } = v;
1569
1843
  const rotation = chassisBody.rotation();
1570
- const quat = new THREE3.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w);
1844
+ const quat = new THREE3.Quaternion(
1845
+ rotation.x,
1846
+ rotation.y,
1847
+ rotation.z,
1848
+ rotation.w
1849
+ );
1571
1850
  const forward = new THREE3.Vector3(1, 0, 0).applyQuaternion(quat);
1572
1851
  const slopeAngle = Math.asin(forward.y);
1573
1852
  let factor = 1;
1574
- if (slopeAngle < -0.05 && this.fwdPressed) {
1575
- factor = -Math.sin(slopeAngle) * 10;
1576
- }
1577
- const engineForce = (Number(this.fwdPressed) * this.vehicleParams.power.accelerateForce - Number(this.bkdPressed) * this.vehicleParams.power.accelerateForce) * factor;
1853
+ if (slopeAngle < -0.05 && this.fwdPressed) factor = -Math.sin(slopeAngle) * 10;
1854
+ const accelerateForce = this.vehicleParams.power.accelerateForce * v.speedMultiplier;
1855
+ const maxSpeed = this.vehicleParams.power.maxSpeed * v.speedMultiplier;
1856
+ const engineForce = (Number(this.fwdPressed) * accelerateForce - Number(this.bkdPressed) * accelerateForce) * factor;
1578
1857
  vehicleController.setWheelEngineForce(0, engineForce);
1579
1858
  vehicleController.setWheelEngineForce(1, engineForce);
1580
1859
  vehicleController.setWheelEngineForce(2, engineForce);
@@ -1594,7 +1873,11 @@ var PlayerController = class {
1594
1873
  }
1595
1874
  const steerLerpFactor = 1 - Math.pow(1 - steerSpeed, delta);
1596
1875
  const targetSteering = this.vehicleParams.steering.maxSteerAngle * steerDirection;
1597
- const steering = THREE3.MathUtils.lerp(currentSteering, targetSteering, steerLerpFactor);
1876
+ const steering = THREE3.MathUtils.lerp(
1877
+ currentSteering,
1878
+ targetSteering,
1879
+ steerLerpFactor
1880
+ );
1598
1881
  vehicleController.setWheelSteering(0, steering);
1599
1882
  vehicleController.setWheelSteering(1, steering);
1600
1883
  if ((this.rgtPressed || this.lftPressed) && this.shiftPressed) {
@@ -1612,36 +1895,56 @@ var PlayerController = class {
1612
1895
  this.camera.position.add(lookTarget);
1613
1896
  this.controls.update();
1614
1897
  const velocity = chassisBody.linvel();
1615
- const currentSpeed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
1616
- const maxSpeed = this.vehicleParams.power.maxSpeed * v.scale;
1898
+ const currentSpeed = Math.sqrt(
1899
+ velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z
1900
+ );
1617
1901
  const speedRatio = Math.min(currentSpeed / maxSpeed, 1);
1618
1902
  const baseCamDistance = v.size.l * 0.8;
1619
1903
  const maxCamDistanceLimit = v.size.l * 5;
1620
- const targetDistance = THREE3.MathUtils.lerp(baseCamDistance, maxCamDistanceLimit, speedRatio);
1621
- this._personToCam.subVectors(this.camera.position, vehicleGroup.position);
1904
+ const targetDistance = THREE3.MathUtils.lerp(
1905
+ baseCamDistance,
1906
+ maxCamDistanceLimit,
1907
+ speedRatio
1908
+ );
1909
+ this._personToCam.subVectors(
1910
+ this.camera.position,
1911
+ vehicleGroup.position
1912
+ );
1622
1913
  const origin = vehicleGroup.position.clone().add(new THREE3.Vector3(0, 0, 0));
1623
1914
  const direction = this._personToCam.clone().normalize();
1624
1915
  const desiredDist = targetDistance;
1625
1916
  this._raycasterPersonToCam.set(origin, direction);
1626
1917
  this._raycasterPersonToCam.far = desiredDist;
1627
- const intersects = this._raycasterPersonToCam.intersectObject(this.collider, false);
1918
+ const intersects = this._raycasterPersonToCam.intersectObject(
1919
+ this.collider,
1920
+ false
1921
+ );
1628
1922
  if (intersects.length > 0) {
1629
1923
  const hit = intersects[0];
1630
- const safeDist = Math.max(hit.distance - this._camEpsilon, this._minCamDistance);
1924
+ const safeDist = Math.max(
1925
+ hit.distance - this._camEpsilon,
1926
+ this.minCamDistance
1927
+ );
1631
1928
  const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
1632
1929
  this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
1633
1930
  } else {
1634
1931
  this._raycasterPersonToCam.far = maxCamDistanceLimit;
1635
- const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(this.collider, false);
1932
+ const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
1933
+ this.collider,
1934
+ false
1935
+ );
1636
1936
  let safeDist = desiredDist;
1637
1937
  if (intersectsMaxDis.length) {
1638
1938
  const hitMax = intersectsMaxDis[0];
1639
- safeDist = Math.min(desiredDist, hitMax.distance - this._camEpsilon);
1939
+ safeDist = Math.min(
1940
+ desiredDist,
1941
+ hitMax.distance - this._camEpsilon
1942
+ );
1640
1943
  }
1641
1944
  const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
1642
1945
  this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
1643
1946
  }
1644
- if (this.fwdPressed || this.bkdPressed) {
1947
+ if ((this.fwdPressed || this.bkdPressed) && this.vehicleParams.followVehicleDirection) {
1645
1948
  const vel = chassisBody.linvel();
1646
1949
  const velHorizontal = new THREE3.Vector3(vel.x, vel.y, vel.z);
1647
1950
  const velSpeed = velHorizontal.length();
@@ -1649,8 +1952,13 @@ var PlayerController = class {
1649
1952
  const targetBehindDir = velHorizontal.clone().normalize().negate();
1650
1953
  this.camBehindDir.lerp(targetBehindDir, this._camCollisionLerp).normalize();
1651
1954
  const camHeightOffset = v.size.h;
1652
- const targetCamPos = lookTarget.clone().add(this.camBehindDir.clone().multiplyScalar(desiredDist)).add(new THREE3.Vector3(0, camHeightOffset, 0));
1653
- this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
1955
+ const targetCamPos = lookTarget.clone().add(
1956
+ this.camBehindDir.clone().multiplyScalar(desiredDist)
1957
+ ).add(new THREE3.Vector3(0, camHeightOffset, 0));
1958
+ this.camera.position.lerp(
1959
+ targetCamPos,
1960
+ this._camCollisionLerp
1961
+ );
1654
1962
  this.controls.update();
1655
1963
  }
1656
1964
  }
@@ -1661,8 +1969,18 @@ var PlayerController = class {
1661
1969
  const size = new THREE3.Vector3();
1662
1970
  v.vehicleBBox?.getSize(size);
1663
1971
  const translation2 = chassisBody.translation();
1664
- chassisBody.setTranslation(new this.RAPIER.Vector3(translation2.x, translation2.y + size.y, translation2.z), true);
1665
- chassisBody.setRotation(new this.RAPIER.Quaternion(0, 0, 0, 1), true);
1972
+ chassisBody.setTranslation(
1973
+ new this.RAPIER.Vector3(
1974
+ translation2.x,
1975
+ translation2.y + size.y,
1976
+ translation2.z
1977
+ ),
1978
+ true
1979
+ );
1980
+ chassisBody.setRotation(
1981
+ new this.RAPIER.Quaternion(0, 0, 0, 1),
1982
+ true
1983
+ );
1666
1984
  chassisBody.setLinvel(new this.RAPIER.Vector3(0, 0, 0), true);
1667
1985
  chassisBody.setAngvel(new this.RAPIER.Vector3(0, 0, 0), true);
1668
1986
  }
@@ -1677,20 +1995,60 @@ var PlayerController = class {
1677
1995
  for (const v of this.vehicles) {
1678
1996
  const { vehicleController, chassisBody, vehicleGroup, updateWheelVisuals } = v;
1679
1997
  vehicleController.updateVehicle(delta);
1998
+ if (chassisBody.isSleeping()) continue;
1680
1999
  const velocity = chassisBody.linvel();
1681
- const currentSpeed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
1682
- const maxSpeed = this.vehicleParams.power.maxSpeed * v.scale;
2000
+ const currentSpeed = Math.sqrt(
2001
+ velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z
2002
+ );
2003
+ const maxSpeed = this.vehicleParams.power.maxSpeed * v.speedMultiplier;
1683
2004
  if (currentSpeed > maxSpeed) {
1684
2005
  const s = maxSpeed / currentSpeed;
1685
- chassisBody.setLinvel(new this.RAPIER.Vector3(velocity.x * s, velocity.y * s, velocity.z * s), true);
2006
+ chassisBody.setLinvel(
2007
+ new this.RAPIER.Vector3(
2008
+ velocity.x * s,
2009
+ velocity.y * s,
2010
+ velocity.z * s
2011
+ ),
2012
+ true
2013
+ );
1686
2014
  }
1687
2015
  const translation = chassisBody.translation();
1688
2016
  const rotationSync = chassisBody.rotation();
1689
- vehicleGroup.position.set(translation.x, translation.y, translation.z);
1690
- vehicleGroup.quaternion.set(rotationSync.x, rotationSync.y, rotationSync.z, rotationSync.w);
2017
+ vehicleGroup.position.set(
2018
+ translation.x,
2019
+ translation.y,
2020
+ translation.z
2021
+ );
2022
+ vehicleGroup.quaternion.set(
2023
+ rotationSync.x,
2024
+ rotationSync.y,
2025
+ rotationSync.z,
2026
+ rotationSync.w
2027
+ );
1691
2028
  if (updateWheelVisuals) updateWheelVisuals();
1692
2029
  }
1693
2030
  }
2031
+ /**
2032
+ * 设置人物缩放
2033
+ */
2034
+ setPlayerScale(newScale) {
2035
+ if (newScale <= 0) return;
2036
+ const ratio = newScale / this.playerModel.scale;
2037
+ this.playerModel.scale = newScale;
2038
+ this.gravity *= ratio;
2039
+ this.jumpHeight *= ratio;
2040
+ this.playerSpeed *= ratio;
2041
+ this.playerFlySpeed *= ratio;
2042
+ this.curPlayerSpeed *= ratio;
2043
+ this._camEpsilon *= ratio;
2044
+ this.minCamDistance *= ratio;
2045
+ this.maxCamDistance *= ratio;
2046
+ this.orginMaxCamDistance *= ratio;
2047
+ if (this.isFirstPerson) this.scene.attach(this.camera);
2048
+ this.player?.scale.multiplyScalar(ratio);
2049
+ if (this.player?.capsuleInfo) this.player.capsuleInfo.radius *= ratio;
2050
+ if (this.isFirstPerson) this.setFirstPersonCamera();
2051
+ }
1694
2052
  /**
1695
2053
  * 更新人物
1696
2054
  */
@@ -1701,7 +2059,40 @@ var PlayerController = class {
1701
2059
  if (!this.isFlying) {
1702
2060
  this.player.position.addScaledVector(this.playerVelocity, delta);
1703
2061
  }
2062
+ if (this.isBoardingAnimPlaying) {
2063
+ const action = this.personActions?.get("enterCar");
2064
+ const duration = action.getClip().duration;
2065
+ const timeScale = action.getEffectiveTimeScale();
2066
+ const remaining = (duration - action.time) / timeScale * 1e3;
2067
+ if (!this.closeDoorTriggered && remaining <= 500) {
2068
+ this.closeDoorTriggered = true;
2069
+ this.openVehicleDoor(false);
2070
+ }
2071
+ if (action.time >= duration) {
2072
+ this.isBoardingAnimPlaying = false;
2073
+ this.closeDoorTriggered = false;
2074
+ this.onEnterCarAnimFinished();
2075
+ return;
2076
+ }
2077
+ }
2078
+ if (this.isExitAnimPlaying) {
2079
+ const action = this.personActions?.get("exitCar");
2080
+ if (action) {
2081
+ const duration = action.getClip().duration;
2082
+ const timeScale = action.getEffectiveTimeScale();
2083
+ const remaining = (duration - action.time) / timeScale * 1e3;
2084
+ if (!this.closeExitDoorTriggered && remaining <= 500) {
2085
+ this.closeExitDoorTriggered = true;
2086
+ this.openVehicleDoor(false);
2087
+ }
2088
+ if (action.time >= duration) {
2089
+ this.isExitAnimPlaying = false;
2090
+ this.closeExitDoorTriggered = false;
2091
+ }
2092
+ }
2093
+ }
1704
2094
  this.updateMixers(delta);
2095
+ if (this.controllerMode === 1) return;
1705
2096
  this.camera.getWorldDirection(this.camDir);
1706
2097
  let angle = Math.atan2(this.camDir.z, this.camDir.x) + Math.PI / 2;
1707
2098
  angle = 2 * Math.PI - angle;
@@ -1716,16 +2107,26 @@ var PlayerController = class {
1716
2107
  if (this.spacePressed) this.moveDir.add(this.DIR_UP);
1717
2108
  }
1718
2109
  if (this.isFlying && this.fwdPressed) {
1719
- this.playerSpeed = this.shiftPressed ? this.originPlayerSpeed * 12 : this.originPlayerSpeed * 7;
2110
+ this.curPlayerSpeed = this.shiftPressed ? this.playerFlySpeed * 2 : this.playerFlySpeed;
1720
2111
  } else {
1721
- this.playerSpeed = this.shiftPressed ? this.originPlayerSpeed * 2 : this.originPlayerSpeed;
2112
+ this.curPlayerSpeed = this.shiftPressed ? this.playerSpeed * 2 : this.playerSpeed;
1722
2113
  }
1723
2114
  this.moveDir.normalize().applyAxisAngle(this.upVector, angle);
1724
- this.player.position.addScaledVector(this.moveDir, this.playerSpeed * delta);
2115
+ this.player.position.addScaledVector(
2116
+ this.moveDir,
2117
+ this.curPlayerSpeed * delta
2118
+ );
1725
2119
  let playerDistanceFromGround = Infinity;
1726
- this._originTmp.set(this.player.position.x, this.player.position.y, this.player.position.z);
2120
+ this._originTmp.set(
2121
+ this.player.position.x,
2122
+ this.player.position.y,
2123
+ this.player.position.z
2124
+ );
1727
2125
  this._raycaster.ray.origin.copy(this._originTmp);
1728
- const intersects = this._raycaster.intersectObject(this.collider, false);
2126
+ const intersects = this._raycaster.intersectObject(
2127
+ this.collider,
2128
+ false
2129
+ );
1729
2130
  if (intersects.length > 0) {
1730
2131
  playerDistanceFromGround = this.player.position.y - intersects[0].point.y;
1731
2132
  const maxH = this.playerHeight * this.playerModel.scale * 0.9;
@@ -1734,7 +2135,10 @@ var PlayerController = class {
1734
2135
  if (!this.isFlying) {
1735
2136
  if (playerDistanceFromGround >= maxH) {
1736
2137
  this.playerVelocity.y += delta * this.gravity;
1737
- this.player.position.addScaledVector(this.playerVelocity, delta);
2138
+ this.player.position.addScaledVector(
2139
+ this.playerVelocity,
2140
+ delta
2141
+ );
1738
2142
  this.playerIsOnGround = false;
1739
2143
  } else if (playerDistanceFromGround >= h && playerDistanceFromGround < maxH) {
1740
2144
  if (!this.spacePressed) {
@@ -1748,7 +2152,11 @@ var PlayerController = class {
1748
2152
  this.player.position.y = intersects[0].point.y + h;
1749
2153
  } else if (playerDistanceFromGround < minH) {
1750
2154
  this.playerVelocity.set(0, 0, 0);
1751
- this.player.position.set(this.player.position.x, intersects[0].point.y + h, this.player.position.z);
2155
+ this.player.position.set(
2156
+ this.player.position.x,
2157
+ intersects[0].point.y + h,
2158
+ this.player.position.z
2159
+ );
1752
2160
  this.playerIsOnGround = true;
1753
2161
  }
1754
2162
  }
@@ -1769,13 +2177,20 @@ var PlayerController = class {
1769
2177
  intersectsTriangle: (tri) => {
1770
2178
  const triPoint = this.tempVector;
1771
2179
  const capsulePoint = this.tempVector2;
1772
- const distance = tri.closestPointToSegment(this.tempSegment, triPoint, capsulePoint);
2180
+ const distance = tri.closestPointToSegment(
2181
+ this.tempSegment,
2182
+ triPoint,
2183
+ capsulePoint
2184
+ );
1773
2185
  if (distance < capsuleInfo.radius) {
1774
2186
  const normal = tri.getNormal(new THREE3.Vector3());
1775
2187
  if (normal.y > 0.5 && !this.isFlying) return;
1776
2188
  const depth = capsuleInfo.radius - distance;
1777
2189
  const direction = capsulePoint.sub(triPoint).normalize();
1778
- this.tempSegment.start.addScaledVector(direction, depth);
2190
+ this.tempSegment.start.addScaledVector(
2191
+ direction,
2192
+ depth
2193
+ );
1779
2194
  this.tempSegment.end.addScaledVector(direction, depth);
1780
2195
  }
1781
2196
  }
@@ -1785,18 +2200,28 @@ var PlayerController = class {
1785
2200
  intersectsTriangle: (tri) => {
1786
2201
  const triPoint = this.tempVector;
1787
2202
  const capsulePoint = this.tempVector2;
1788
- const distance = tri.closestPointToSegment(this.tempSegment, triPoint, capsulePoint);
2203
+ const distance = tri.closestPointToSegment(
2204
+ this.tempSegment,
2205
+ triPoint,
2206
+ capsulePoint
2207
+ );
1789
2208
  if (distance < capsuleInfo.radius) {
1790
2209
  const depth = capsuleInfo.radius - distance;
1791
2210
  const direction = capsulePoint.sub(triPoint).normalize();
1792
- this.tempSegment.start.addScaledVector(direction, depth);
2211
+ this.tempSegment.start.addScaledVector(
2212
+ direction,
2213
+ depth
2214
+ );
1793
2215
  this.tempSegment.end.addScaledVector(direction, depth);
1794
2216
  }
1795
2217
  }
1796
2218
  });
1797
2219
  }
1798
2220
  const newPosition = this.tempVector.copy(this.tempSegment.start).applyMatrix4(this.collider.matrixWorld);
1799
- const deltaVector = this.tempVector2.subVectors(newPosition, this.player.position);
2221
+ const deltaVector = this.tempVector2.subVectors(
2222
+ newPosition,
2223
+ this.player.position
2224
+ );
1800
2225
  const offset = Math.max(0, deltaVector.length() - 1e-5);
1801
2226
  deltaVector.normalize().multiplyScalar(offset);
1802
2227
  this.player.position.add(deltaVector);
@@ -1813,14 +2238,22 @@ var PlayerController = class {
1813
2238
  } else {
1814
2239
  lookTarget = this.player.position.clone().add(this.camDir);
1815
2240
  }
1816
- this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);
2241
+ this.targetMat.lookAt(
2242
+ this.player.position,
2243
+ lookTarget,
2244
+ this.player.up
2245
+ );
1817
2246
  this.targetQuat.setFromRotationMatrix(this.targetMat);
1818
2247
  const alpha = Math.min(1, this.rotationSpeed * delta);
1819
2248
  this.player.quaternion.slerp(this.targetQuat, alpha);
1820
2249
  }
1821
2250
  if ((this.thirdMouseMode === 1 || this.thirdMouseMode === 3) && this.moveDir.lengthSq() > 0) {
1822
2251
  lookTarget = this.player.position.clone().add(this.moveDir);
1823
- this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);
2252
+ this.targetMat.lookAt(
2253
+ this.player.position,
2254
+ lookTarget,
2255
+ this.player.up
2256
+ );
1824
2257
  this.targetQuat.setFromRotationMatrix(this.targetMat);
1825
2258
  const alpha = Math.min(1, this.rotationSpeed * delta);
1826
2259
  this.player.quaternion.slerp(this.targetQuat, alpha);
@@ -1834,7 +2267,11 @@ var PlayerController = class {
1834
2267
  this.moveDir.normalize();
1835
2268
  this.moveDir.negate();
1836
2269
  const lookTarget = this.player.position.clone().add(this.fwdPressed ? this.moveDir : this.camDir);
1837
- this.targetMat.lookAt(this.player.position, lookTarget, this.player.up);
2270
+ this.targetMat.lookAt(
2271
+ this.player.position,
2272
+ lookTarget,
2273
+ this.player.up
2274
+ );
1838
2275
  this.targetQuat.setFromRotationMatrix(this.targetMat);
1839
2276
  const alpha = Math.min(1, this.rotationSpeed * delta);
1840
2277
  this.player.quaternion.slerp(this.targetQuat, alpha);
@@ -1842,50 +2279,119 @@ var PlayerController = class {
1842
2279
  }
1843
2280
  if (!this.isFirstPerson) {
1844
2281
  const lookTarget = this.player.position.clone();
1845
- lookTarget.y += this.playerHeight / 6 * this.playerModel.scale;
2282
+ lookTarget.y += this.playerHeight / 8 * this.playerModel.scale;
1846
2283
  this.camera.position.sub(this.controls.target);
1847
2284
  this.controls.target.copy(lookTarget);
1848
2285
  this.camera.position.add(lookTarget);
1849
2286
  this.controls.update();
1850
2287
  if (!this.enableZoom) {
1851
- this._personToCam.subVectors(this.camera.position, this.player.position);
2288
+ this._personToCam.subVectors(
2289
+ this.camera.position,
2290
+ this.player.position
2291
+ );
1852
2292
  const origin = this.player.position.clone();
1853
2293
  const direction = this._personToCam.clone().normalize();
1854
2294
  const desiredDist = this._personToCam.length();
1855
2295
  this._raycasterPersonToCam.set(origin, direction);
1856
2296
  this._raycasterPersonToCam.far = desiredDist;
1857
- const intersectsCamera = this._raycasterPersonToCam.intersectObject(this.collider, false);
2297
+ const intersectsCamera = this._raycasterPersonToCam.intersectObject(
2298
+ this.collider,
2299
+ false
2300
+ );
1858
2301
  if (intersectsCamera.length > 0) {
1859
2302
  const hit = intersectsCamera[0];
1860
- const safeDist = Math.max(hit.distance - this._camEpsilon, this._minCamDistance);
2303
+ const safeDist = Math.max(
2304
+ hit.distance - this._camEpsilon,
2305
+ this.minCamDistance
2306
+ );
1861
2307
  const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
1862
- this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
2308
+ this.camera.position.lerp(
2309
+ targetCamPos,
2310
+ this._camCollisionLerp
2311
+ );
1863
2312
  } else {
1864
- this._raycasterPersonToCam.far = this._maxCamDistance;
1865
- const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(this.collider, false);
1866
- let safeDist = this._maxCamDistance;
2313
+ this._raycasterPersonToCam.far = this.maxCamDistance;
2314
+ const intersectsMaxDis = this._raycasterPersonToCam.intersectObject(
2315
+ this.collider,
2316
+ false
2317
+ );
2318
+ let safeDist = this.maxCamDistance;
1867
2319
  if (intersectsMaxDis.length) {
1868
2320
  const hitMax = intersectsMaxDis[0];
1869
2321
  safeDist = hitMax.distance - this._camEpsilon;
1870
2322
  }
1871
2323
  const targetCamPos = origin.clone().add(direction.clone().multiplyScalar(safeDist));
1872
- this.camera.position.lerp(targetCamPos, this._camCollisionLerp);
2324
+ this.camera.position.lerp(
2325
+ targetCamPos,
2326
+ this._camCollisionLerp
2327
+ );
1873
2328
  }
1874
2329
  }
1875
2330
  }
1876
2331
  if (this.player.position.y < this.boundingBoxMinY - 1) {
1877
- this._originTmp.set(this.player.position.x, 1e4, this.player.position.z);
2332
+ this._originTmp.set(
2333
+ this.player.position.x,
2334
+ 1e4,
2335
+ this.player.position.z
2336
+ );
1878
2337
  this._raycaster.ray.origin.copy(this._originTmp);
1879
- const intersectsFall = this._raycaster.intersectObject(this.collider, false);
2338
+ const intersectsFall = this._raycaster.intersectObject(
2339
+ this.collider,
2340
+ false
2341
+ );
1880
2342
  if (intersectsFall.length > 0) {
1881
2343
  console.log("\u73A9\u5BB6\u4E3Abug\u610F\u5916\u6389\u843D");
1882
- this.reset(new THREE3.Vector3(this.player.position.x, intersectsFall[0].point.y + 5, this.player.position.z));
2344
+ this.reset(
2345
+ new THREE3.Vector3(
2346
+ this.player.position.x,
2347
+ intersectsFall[0].point.y + 5,
2348
+ this.player.position.z
2349
+ )
2350
+ );
1883
2351
  } else {
1884
2352
  console.log("\u73A9\u5BB6\u6B63\u5E38\u6389\u843D");
1885
- this.reset(new THREE3.Vector3(this.player.position.x, this.player.position.y + 15, this.player.position.z));
2353
+ this.reset(
2354
+ new THREE3.Vector3(
2355
+ this.player.position.x,
2356
+ this.player.position.y + 15,
2357
+ this.player.position.z
2358
+ )
2359
+ );
2360
+ }
2361
+ }
2362
+ if (this.isShowMobileControls) {
2363
+ if (this.vehicles.length) {
2364
+ let near = false;
2365
+ for (const v of this.vehicles) {
2366
+ this.nearCheckLocal.copy(v.boardingPoint).multiplyScalar(v.scale);
2367
+ v.vehicleGroup.localToWorld(
2368
+ this.nearCheckWorld.copy(this.nearCheckLocal)
2369
+ );
2370
+ if (this.player.position.distanceTo(this.nearCheckWorld) < 800 * this.playerModel.scale) {
2371
+ near = true;
2372
+ this.syncVehicleBtnEl(near);
2373
+ break;
2374
+ }
2375
+ }
2376
+ if (near !== this.isNearVehicle) {
2377
+ this.isNearVehicle = near;
2378
+ this.syncVehicleBtnEl(near);
2379
+ }
2380
+ } else {
2381
+ this.isNearVehicle = false;
2382
+ this.syncVehicleBtnEl(false);
1886
2383
  }
1887
2384
  }
1888
2385
  }
2386
+ /**
2387
+ * 获取屏幕中心点向前射线与碰撞体的交点
2388
+ */
2389
+ getCenterScreenRaycastHit() {
2390
+ this.camera.updateMatrixWorld();
2391
+ this.centerRay.setFromCamera(this.centerMouse, this.camera);
2392
+ const intersects = this.centerRay.intersectObject(this.collider, false);
2393
+ return intersects[0];
2394
+ }
1889
2395
  /**
1890
2396
  * 更新模型动画
1891
2397
  */
@@ -1901,7 +2407,7 @@ var PlayerController = class {
1901
2407
  this.player.position.copy(position ?? this.initPos);
1902
2408
  }
1903
2409
  getPosition() {
1904
- return this.player.position;
2410
+ return this.player?.position;
1905
2411
  }
1906
2412
  // ==================== 输入处理 ====================
1907
2413
  setInput(input) {
@@ -1920,7 +2426,14 @@ var PlayerController = class {
1920
2426
  }
1921
2427
  if (typeof input.jump === "boolean") {
1922
2428
  if (input.jump) {
2429
+ if (this.isMovingToBoardingPoint) {
2430
+ this.isMovingToBoardingPoint = false;
2431
+ this.boardingWaypoints = [];
2432
+ this.currentWaypointIndex = 0;
2433
+ this.boardingTargetDir = null;
2434
+ }
1923
2435
  this.spacePressed = true;
2436
+ if (this.controllerMode == 1) return;
1924
2437
  if (!this.playerIsOnGround || this.isFlying) return;
1925
2438
  this.playPersonAnimationByName("jumping");
1926
2439
  this.playerVelocity.y = this.jumpHeight;
@@ -1935,13 +2448,20 @@ var PlayerController = class {
1935
2448
  if (input.toggleView) {
1936
2449
  this.changeView();
1937
2450
  }
1938
- if (input.toggleFly) {
2451
+ if (input.toggleFly && this.playerFlyEnabled && this.controllerMode == 0) {
1939
2452
  this.isFlying = !this.isFlying;
1940
2453
  this.setAnimationByPressed();
1941
2454
  if (!this.isFlying && !this.playerIsOnGround) {
1942
2455
  this.playPersonAnimationByName("jumping");
1943
2456
  }
1944
2457
  }
2458
+ if (input.toggleVehicle) {
2459
+ if (this.controllerMode == 0) {
2460
+ this.enterVehicle();
2461
+ } else {
2462
+ this.exitVehicle();
2463
+ }
2464
+ }
1945
2465
  }
1946
2466
  onAllEvent() {
1947
2467
  this.isupdate = true;
@@ -1981,11 +2501,17 @@ var PlayerController = class {
1981
2501
  userSelect: "none"
1982
2502
  });
1983
2503
  container.appendChild(this.joystickZoneEl);
1984
- ["touchstart", "touchmove", "touchend", "touchcancel"].forEach((evtName) => {
1985
- this.joystickZoneEl?.addEventListener(evtName, (e) => e.preventDefault(), {
1986
- passive: false
1987
- });
1988
- });
2504
+ ["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
2505
+ (evtName) => {
2506
+ this.joystickZoneEl?.addEventListener(
2507
+ evtName,
2508
+ (e) => e.preventDefault(),
2509
+ {
2510
+ passive: false
2511
+ }
2512
+ );
2513
+ }
2514
+ );
1989
2515
  this.joystickManager = nipple.create({
1990
2516
  zone: this.joystickZoneEl,
1991
2517
  mode: "static",
@@ -2008,13 +2534,22 @@ var PlayerController = class {
2008
2534
  const dirY = rawY > deadzone ? 1 : rawY < -deadzone ? -1 : 0;
2009
2535
  const sprintThreshold = JOY_SIZE / 2;
2010
2536
  const isSprinting = distance >= sprintThreshold;
2011
- const prev = this.prevJoyState || { dirX: 0, dirY: 0, shift: false };
2012
- if (dirX === prev.dirX && dirY === prev.dirY && isSprinting === prev.shift) return;
2537
+ const prev = this.prevJoyState || {
2538
+ dirX: 0,
2539
+ dirY: 0,
2540
+ shift: false
2541
+ };
2542
+ if (dirX === prev.dirX && dirY === prev.dirY && isSprinting === prev.shift)
2543
+ return;
2013
2544
  this.prevJoyState = { dirX, dirY, shift: isSprinting };
2014
2545
  this.setInput({ moveX: dirX, moveY: dirY, shift: isSprinting });
2015
2546
  });
2016
2547
  this.joystickManager.on("end", () => {
2017
- const prev = this.prevJoyState || { dirX: 0, dirY: 0, shift: false };
2548
+ const prev = this.prevJoyState || {
2549
+ dirX: 0,
2550
+ dirY: 0,
2551
+ shift: false
2552
+ };
2018
2553
  if (prev.dirX !== 0 || prev.dirY !== 0 || prev.shift !== false) {
2019
2554
  this.prevJoyState = { dirX: 0, dirY: 0, shift: false };
2020
2555
  this.setInput({ moveX: 0, moveY: 0, shift: false });
@@ -2033,15 +2568,29 @@ var PlayerController = class {
2033
2568
  userSelect: "none"
2034
2569
  });
2035
2570
  container.appendChild(this.lookAreaEl);
2036
- ["touchstart", "touchmove", "touchend", "touchcancel"].forEach((evtName) => {
2037
- this.lookAreaEl?.addEventListener(evtName, (e) => e.preventDefault(), {
2038
- passive: false
2039
- });
2571
+ ["touchstart", "touchmove", "touchend", "touchcancel"].forEach(
2572
+ (evtName) => {
2573
+ this.lookAreaEl?.addEventListener(
2574
+ evtName,
2575
+ (e) => e.preventDefault(),
2576
+ {
2577
+ passive: false
2578
+ }
2579
+ );
2580
+ }
2581
+ );
2582
+ this.lookAreaEl.addEventListener("pointerdown", this.onPointerDown, {
2583
+ passive: false
2584
+ });
2585
+ this.lookAreaEl.addEventListener("pointermove", this.onPointerMove, {
2586
+ passive: false
2587
+ });
2588
+ this.lookAreaEl.addEventListener("pointerup", this.onPointerUp, {
2589
+ passive: false
2590
+ });
2591
+ this.lookAreaEl.addEventListener("pointercancel", this.onPointerUp, {
2592
+ passive: false
2040
2593
  });
2041
- this.lookAreaEl.addEventListener("pointerdown", this.onPointerDown, { passive: false });
2042
- this.lookAreaEl.addEventListener("pointermove", this.onPointerMove, { passive: false });
2043
- this.lookAreaEl.addEventListener("pointerup", this.onPointerUp, { passive: false });
2044
- this.lookAreaEl.addEventListener("pointercancel", this.onPointerUp, { passive: false });
2045
2594
  const createBtn = (rightPx, bottomPx, bgUrl) => {
2046
2595
  const btn = document.createElement("button");
2047
2596
  const styles = {
@@ -2073,7 +2622,9 @@ var PlayerController = class {
2073
2622
  Object.assign(btn.style, styles);
2074
2623
  container.appendChild(btn);
2075
2624
  ["touchstart", "touchend", "touchcancel"].forEach((evtName) => {
2076
- btn.addEventListener(evtName, (e) => e.preventDefault(), { passive: false });
2625
+ btn.addEventListener(evtName, (e) => e.preventDefault(), {
2626
+ passive: false
2627
+ });
2077
2628
  });
2078
2629
  return btn;
2079
2630
  };
@@ -2120,6 +2671,15 @@ var PlayerController = class {
2120
2671
  },
2121
2672
  { passive: false }
2122
2673
  );
2674
+ this.vehicleBtnEl = createBtn(14 + 100, 14 + 120, vehicle_default);
2675
+ this.vehicleBtnEl.addEventListener(
2676
+ "touchstart",
2677
+ (e) => {
2678
+ e.preventDefault();
2679
+ this.setInput({ toggleVehicle: true });
2680
+ },
2681
+ { passive: false }
2682
+ );
2123
2683
  }
2124
2684
  destroyMobileControls() {
2125
2685
  try {
@@ -2128,7 +2688,9 @@ var PlayerController = class {
2128
2688
  this.joystickManager = null;
2129
2689
  }
2130
2690
  if (this.joystickZoneEl?.parentElement) {
2131
- this.joystickZoneEl.parentElement.removeChild(this.joystickZoneEl);
2691
+ this.joystickZoneEl.parentElement.removeChild(
2692
+ this.joystickZoneEl
2693
+ );
2132
2694
  this.joystickZoneEl = null;
2133
2695
  }
2134
2696
  if (this.lookAreaEl?.parentElement) {
@@ -2147,14 +2709,86 @@ var PlayerController = class {
2147
2709
  this.viewBtnEl.parentElement.removeChild(this.viewBtnEl);
2148
2710
  this.viewBtnEl = null;
2149
2711
  }
2150
- this.lookAreaEl?.removeEventListener("pointerdown", this.onPointerDown);
2151
- this.lookAreaEl?.removeEventListener("pointermove", this.onPointerMove);
2712
+ if (this.vehicleBtnEl?.parentElement) {
2713
+ this.vehicleBtnEl.parentElement.removeChild(this.vehicleBtnEl);
2714
+ this.vehicleBtnEl = null;
2715
+ }
2716
+ this.lookAreaEl?.removeEventListener(
2717
+ "pointerdown",
2718
+ this.onPointerDown
2719
+ );
2720
+ this.lookAreaEl?.removeEventListener(
2721
+ "pointermove",
2722
+ this.onPointerMove
2723
+ );
2152
2724
  this.lookAreaEl?.removeEventListener("pointerup", this.onPointerUp);
2153
- this.lookAreaEl?.removeEventListener("pointercancel", this.onPointerUp);
2725
+ this.lookAreaEl?.removeEventListener(
2726
+ "pointercancel",
2727
+ this.onPointerUp
2728
+ );
2154
2729
  } catch (e) {
2155
2730
  console.warn("\u9500\u6BC1\u79FB\u52A8\u7AEF\u6447\u6746\u63A7\u5236\u65F6\u51FA\u9519\uFF1A", e);
2156
2731
  }
2157
2732
  }
2733
+ syncVehicleBtnEl(show) {
2734
+ if (!this.vehicleBtnEl) return;
2735
+ this.vehicleBtnEl.style.display = show ? "block" : "none";
2736
+ }
2737
+ syncControllerModeBtnEl() {
2738
+ if (!this.isShowMobileControls) return;
2739
+ if (this.controllerMode == 0) {
2740
+ this.flyBtnEl.style.display = "block";
2741
+ const overlayColor = "rgba(0,0,0,0.5)";
2742
+ this.jumpBtnEl.style.backgroundImage = `linear-gradient(${overlayColor}, ${overlayColor}), url("${jump_default}")`;
2743
+ } else {
2744
+ this.flyBtnEl.style.display = "none";
2745
+ const overlayColor = "rgba(0,0,0,0.5)";
2746
+ this.jumpBtnEl.style.backgroundImage = `linear-gradient(${overlayColor}, ${overlayColor}), url("${break_default}")`;
2747
+ this.jumpBtnEl.style.backgroundImage = `url(${break_default})`;
2748
+ }
2749
+ }
2750
+ // ==================== 更新参数 ====================
2751
+ setMouseSensitivity(mouseSensity) {
2752
+ this.mouseSensity = mouseSensity;
2753
+ this.controls.rotateSpeed = this.mouseSensity * 0.05;
2754
+ }
2755
+ setGravity(gravity) {
2756
+ this.gravity = gravity * this.playerModel.scale;
2757
+ }
2758
+ setJumpHeight(jumpHeight) {
2759
+ this.jumpHeight = jumpHeight * this.playerModel.scale;
2760
+ }
2761
+ setPlayerSpeed(playerSpeed) {
2762
+ this.playerSpeed = playerSpeed * this.playerModel.scale;
2763
+ this.curPlayerSpeed = this.playerSpeed;
2764
+ }
2765
+ setPlayerFlySpeed(playerFlySpeed) {
2766
+ this.playerFlySpeed = playerFlySpeed * this.playerModel.scale;
2767
+ }
2768
+ setMinCamDistance(minCamDistance) {
2769
+ this.minCamDistance = minCamDistance * this.playerModel.scale;
2770
+ }
2771
+ setMaxCamDistance(maxCamDistance) {
2772
+ this.maxCamDistance = maxCamDistance * this.playerModel.scale;
2773
+ this.orginMaxCamDistance = this.maxCamDistance;
2774
+ }
2775
+ setThirdMouseMode(thirdMouseMode) {
2776
+ this.thirdMouseMode = thirdMouseMode;
2777
+ this.setPointerLock();
2778
+ }
2779
+ setEnableZoom(enableZoom) {
2780
+ this.enableZoom = enableZoom;
2781
+ this.controls.enableZoom = this.enableZoom;
2782
+ }
2783
+ setDebug(debug) {
2784
+ if (this.collider) this.scene.remove(this.collider);
2785
+ if (debug) {
2786
+ this.scene.add(this.collider);
2787
+ this.player.material.opacity = 0.5;
2788
+ } else {
2789
+ this.player.material.opacity = 0;
2790
+ }
2791
+ }
2158
2792
  // ==================== 销毁 ====================
2159
2793
  destroy() {
2160
2794
  this.offAllEvent();
@@ -2197,10 +2831,24 @@ function playerController() {
2197
2831
  update: (dt) => c.update(dt),
2198
2832
  destroy: () => c.destroy(),
2199
2833
  setInput: (i) => c.setInput(i),
2200
- getposition: () => c.getPosition(),
2834
+ getPosition: () => c.getPosition(),
2835
+ getCenterScreenRaycastHit: () => c.getCenterScreenRaycastHit(),
2201
2836
  getPerson: () => c.person,
2202
2837
  getActiveVehicle: () => c.activeVehicle,
2203
- getAllVehicles: () => c.vehicles
2838
+ getAllVehicles: () => c.vehicles,
2839
+ switchPlayerModel: (model) => c.switchPlayerModel(model),
2840
+ setMouseSensitivity: (mouseSensity) => c.setMouseSensitivity(mouseSensity),
2841
+ setGravity: (gravity) => c.setGravity(gravity),
2842
+ setJumpHeight: (jumpHeight) => c.setJumpHeight(jumpHeight),
2843
+ setPlayerSpeed: (playerSpeed) => c.setPlayerSpeed(playerSpeed),
2844
+ setPlayerFlySpeed: (playerFlySpeed) => c.setPlayerFlySpeed(playerFlySpeed),
2845
+ setMinCamDistance: (minCamDistance) => c.setMinCamDistance(minCamDistance),
2846
+ setMaxCamDistance: (maxCamDistance) => c.setMaxCamDistance(maxCamDistance),
2847
+ setThirdMouseMode: (thirdMouseMode) => c.setThirdMouseMode(thirdMouseMode),
2848
+ setEnableZoom: (enableZoom) => c.setEnableZoom(enableZoom),
2849
+ setDebug: (debug) => c.setDebug(debug),
2850
+ setOverShoulderView: (enable) => c.setOverShoulderView(enable),
2851
+ setPlayerScale: (scale) => c.setPlayerScale(scale)
2204
2852
  };
2205
2853
  }
2206
2854
  function onAllEvent() {