From 6782a42ba3aec1c7b9942866acb7ce2777761f74 Mon Sep 17 00:00:00 2001 From: min Date: Sun, 14 Jun 2026 21:07:44 +0300 Subject: [PATCH] =?UTF-8?q?feat(anim):=20=D0=BF=D1=80=D1=8B=D0=B6=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=20=D0=B1=D0=B5=D0=B3=D0=B5=20(jump=5Frun=203=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B7=D1=8B,=20Shift+=D0=B4=D0=B2=D0=B8=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - jump_run_anticipate/air/land из Mixamo Running Jump - _jumpKind=run когда Shift+WASD в момент Space - speedRatio=0.71 для jump_run_air (синхрон 0.73с) - три типа: in_place / forward (шаг) / run (бег) Co-Authored-By: Claude Opus 4.7 --- src/engine/MixamoAnimator.js | 8 +++++++ src/engine/PlayerController.js | 42 ++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/engine/MixamoAnimator.js b/src/engine/MixamoAnimator.js index 2b537f3..aaadd9b 100644 --- a/src/engine/MixamoAnimator.js +++ b/src/engine/MixamoAnimator.js @@ -53,6 +53,7 @@ const BASE_STATES = ["idle", "walk", "run", "jump", "fall"]; const EXTRA_STATES = [ "jump_anticipate", "jump_air", "jump_land", "jump_fwd_anticipate", "jump_fwd_air", "jump_fwd_land", + "jump_run_anticipate", "jump_run_air", "jump_run_land", "walk_backward", "run_backward", "run_to_stop", "run_slide", "jump_forward", "jump_backward", "jump_down", "crouch_enter", "crouch_idle", "crouch_walk", "crouch_to_stand", @@ -278,6 +279,7 @@ export class MixamoAnimator { const PHASES = new Set([ 'jump_anticipate', 'jump_land', 'jump_fwd_anticipate', 'jump_fwd_land', + 'jump_run_anticipate', 'jump_run_land', ]); if (!PHASES.has(state)) { continue; @@ -334,6 +336,7 @@ export class MixamoAnimator { "jump", "jump_forward", "jump_backward", "jump_down", "jump_anticipate", "jump_land", "jump_fwd_anticipate", "jump_fwd_air", "jump_fwd_land", + "jump_run_anticipate", "jump_run_air", "jump_run_land", "crouch_enter", "crouch_to_stand", "hit_react", "die_forward", "die_back", "throw_action", "pickup", "push_button", "open_door", @@ -382,6 +385,7 @@ export class MixamoAnimator { const JUMP_STATES = new Set([ 'jump_air', 'jump_land', 'jump_in_place', 'jump_anticipate', 'jump_fwd_anticipate', 'jump_fwd_air', 'jump_fwd_land', + 'jump_run_anticipate', 'jump_run_air', 'jump_run_land', ]); if (JUMP_STATES.has(this._currentState) && !JUMP_STATES.has(state) && this._restPositions) { @@ -402,6 +406,7 @@ export class MixamoAnimator { const JUMP_VITAL = new Set([ 'jump', 'fall', 'jump_air', 'jump_land', 'jump_anticipate', 'jump_fwd_anticipate', 'jump_fwd_air', 'jump_fwd_land', + 'jump_run_anticipate', 'jump_run_air', 'jump_run_land', ]); const isVitalSwitch = JUMP_VITAL.has(state) || JUMP_VITAL.has(this._currentState) @@ -448,8 +453,11 @@ export class MixamoAnimator { // Per-state speedRatio: подгоняем длительность под физику. // jump_fwd_air: Mixamo Jump полёт = 0.43с, физика = 0.73с // → speedRatio = 0.59 (замедлить чтобы клип не зациклился). + // jump_fwd_air: Mixamo Jump полёт 0.43с, физика 0.73с → 0.59 + // jump_run_air: Mixamo Running Jump полёт 0.52с, физика 0.73с → 0.71 const SPEED_RATIO = { jump_fwd_air: 0.59, + jump_run_air: 0.71, }; const speedRatio = SPEED_RATIO[state] || 1.0; // Запустить новую анимацию. Babylon 7 ВНИМАНИЕ: параметр loop diff --git a/src/engine/PlayerController.js b/src/engine/PlayerController.js index 78d4cd1..783e75f 100644 --- a/src/engine/PlayerController.js +++ b/src/engine/PlayerController.js @@ -3101,9 +3101,16 @@ export class PlayerController { || cc.has('KeyA') || cc.has('KeyD') || cc.has('ArrowUp') || cc.has('ArrowDown') || cc.has('ArrowLeft') || cc.has('ArrowRight')); - this._jumpKind = wasdHeld ? 'forward' : 'in_place'; + // in_place — нет WASD + // forward — WASD без Shift (Mixamo Jump) + // run — WASD + Shift (Mixamo Running Jump) + const sprinting = this._shift && !this._crouching; + if (!wasdHeld) this._jumpKind = 'in_place'; + else if (sprinting) this._jumpKind = 'run'; + else this._jumpKind = 'forward'; // anticipate-фаза разной длительности. - const antDuration = this._jumpKind === 'forward' ? 170 : 375; + const antDuration = this._jumpKind === 'in_place' ? 375 + : this._jumpKind === 'run' ? 125 : 170; this._jumpHeld = true; this._coyoteLeft = 0; this._jumpAnticipateUntil = Date.now() + antDuration; @@ -3351,13 +3358,28 @@ export class PlayerController { const inCrouchTransition = this._crouchTransitionUntil && now < this._crouchTransitionUntil; // 3-фазная анимация прыжка. Выбираем семейство фаз по _jumpKind: - // in_place: jump_anticipate/jump_air/jump_land (Mixamo Jumping) - // forward: jump_fwd_anticipate/jump_fwd_air/jump_fwd_land (Mixamo Jump) - const isForward = this._jumpKind === 'forward'; - const stAnticipate = isForward ? 'jump_fwd_anticipate' : 'jump_anticipate'; - const stAir = isForward ? 'jump_fwd_air' : 'jump_air'; - const stLand = isForward ? 'jump_fwd_land' : 'jump_land'; - const landDuration = isForward ? 142 : 570; + // in_place: jump_* (Mixamo Jumping) + // forward: jump_fwd_* (Mixamo Jump, прыжок с шага) + // run: jump_run_* (Mixamo Running Jump, прыжок с бега) + const jk = this._jumpKind; + const isAirborneJump = jk === 'forward' || jk === 'run'; + let stAnticipate, stAir, stLand, landDuration; + if (jk === 'run') { + stAnticipate = 'jump_run_anticipate'; + stAir = 'jump_run_air'; + stLand = 'jump_run_land'; + landDuration = 175; + } else if (jk === 'forward') { + stAnticipate = 'jump_fwd_anticipate'; + stAir = 'jump_fwd_air'; + stLand = 'jump_fwd_land'; + landDuration = 142; + } else { + stAnticipate = 'jump_anticipate'; + stAir = 'jump_air'; + stLand = 'jump_land'; + landDuration = 570; + } const inAnticipate = this._jumpAnticipateUntil && now < this._jumpAnticipateUntil && this._jumpPendingImpulse; @@ -3378,7 +3400,7 @@ export class PlayerController { } else if (inJumpLand) { // Для forward — доигрываем land даже при движении // (там короткая фаза 142мс) - if (isForward || !isMoving) mState = stLand; + if (isAirborneJump || !isMoving) mState = stLand; } else if (this._crouchEnterPending && inCrouchTransition && !isMoving) { mState = 'crouch_enter'; } else if (this._crouchExitPending && inCrouchTransition && !isMoving) {