From 0417d60bdd78cc88133561561fe9469ae22adff0 Mon Sep 17 00:00:00 2001 From: min Date: Fri, 5 Jun 2026 19:47:29 +0300 Subject: [PATCH] =?UTF-8?q?fix(player):=20NPC=20=D0=B0=D0=BD=D0=B8=D0=BC?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D1=85=D0=BE=D0=B4=D1=8C=D0=B1=D1=8B?= =?UTF-8?q?=20+=20=D0=BA=D0=BE=D1=80=D0=BE=D1=82=D0=BA=D0=B8=D0=B5=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BD=D0=B8=20(=D0=BF=D0=BE=D1=80=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- src/engine/BabylonScene.js | 10 +++++----- src/engine/NpcManager.js | 13 +++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/engine/BabylonScene.js b/src/engine/BabylonScene.js index 51cfc10..9017ecf 100644 --- a/src/engine/BabylonScene.js +++ b/src/engine/BabylonScene.js @@ -1735,9 +1735,9 @@ export class BabylonScene { const csm = new CascadedShadowGenerator(size, this._sunLight); csm.numCascades = numCascades; csm.stabilizeCascades = true; - csm.lambda = 0.8; - csm.cascadeBlendPercentage = 0.07; - csm.shadowMaxZ = (q === 'high') ? 200 : 120; + csm.lambda = 0.6; + csm.cascadeBlendPercentage = 0.1; + csm.shadowMaxZ = (q === 'high') ? 90 : 60; csm.bias = PCF_BIAS; csm.normalBias = PCF_NORMAL_BIAS; csm.usePercentageCloserFiltering = true; @@ -1745,8 +1745,8 @@ export class BabylonScene { ? ShadowGenerator.QUALITY_HIGH : ShadowGenerator.QUALITY_MEDIUM; csm.darkness = 0.4; - csm.autoCalcDepthBounds = true; - csm.frustumEdgeFalloff = 8; // убирает «полосу-хвост» тени игрока + csm.autoCalcDepthBounds = false; + csm.frustumEdgeFalloff = 12; // убирает «полосу-хвост» тени игрока this._shadowGenerator = csm; } else { // Обычный ShadowGenerator. Soft теперь 2048 (было 1024). diff --git a/src/engine/NpcManager.js b/src/engine/NpcManager.js index 167c6b4..e6a7b9bf 100644 --- a/src/engine/NpcManager.js +++ b/src/engine/NpcManager.js @@ -391,13 +391,18 @@ export class NpcManager { if (root._isWorldMatrixFrozen) { try { root.unfreezeWorldMatrix(); } catch (e) {} } - root.position.set(npc.x, npc.y, npc.z); + // Процедурная анимация ходьбы (у Kenney-моделей нет скелета). + if (moving) npc.walkPhase += dt * 10; + let bobY = 0, lean = 0; + if (moving && !npc.r15Animator) { + bobY = Math.abs(Math.sin(npc.walkPhase)) * 0.12; + lean = Math.sin(npc.walkPhase) * 0.08; + } + root.position.set(npc.x, npc.y + bobY, npc.z); root.rotation.y = npc.yaw; + root.rotation.z = lean; // data.x/y/z — чтобы scene.find/getPosition видели NPC. data.x = npc.x; data.y = npc.y; data.z = npc.z; - - // Анимация ходьбы — простое покачивание (без R15-скелета у Kenney). - if (moving) npc.walkPhase += dt * 6; // R15-NPC (skin_*): процедурная анимация бега/покоя через R15Animator. if (npc.r15Animator) { try {