From 931d53b4d91fec5bf4be1822783844a6996b17ab Mon Sep 17 00:00:00 2001 From: min Date: Sun, 7 Jun 2026 14:01:14 +0300 Subject: [PATCH] =?UTF-8?q?fix(studio):=20=D0=B1=D0=BB=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D1=80=20=D0=BE=D1=82=203-=D0=B3=D0=BE=20=D0=BB=D0=B8?= =?UTF-8?q?=D1=86=D0=B0=20=D1=81=D1=82=D1=80=D0=B5=D0=BB=D1=8F=D0=B5=D1=82?= =?UTF-8?q?=20=D0=B2=20=D1=82=D0=BE=D1=87=D0=BA=D1=83=20=D0=BA=D0=BB=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0,=20=D0=B0=20=D0=BD=D0=B5=20=D0=B2=20=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D1=80=20=D0=BA=D0=B0=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit При свободном курсоре (нет pointer-lock, 3-е лицо) выстрел шёл из getForwardRay (фокус камеры). Теперь onDown берёт координаты клика → setAimScreenPoint → луч через точку клика; onMove обновляет _holdAim для авто-огня при удержании. При pointer-lock (1-е лицо, курсор в центре) — прежнее поведение (центр). Co-Authored-By: Claude Opus 4.8 --- src/editor/engine/WeaponSystem.js | 33 ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/editor/engine/WeaponSystem.js b/src/editor/engine/WeaponSystem.js index a4bf657..048d848 100644 --- a/src/editor/engine/WeaponSystem.js +++ b/src/editor/engine/WeaponSystem.js @@ -90,6 +90,18 @@ export class WeaponSystem { if (e.button !== 0) return; // Если UI-режим курсора — не стреляем (мышь работает по GUI) if (this.scene3d?.player?.isUiCursorMode?.()) return; + // Если курсор СВОБОДЕН (нет pointer-lock — обычно 3-е лицо) — стреляем + // ТУДА, КУДА КЛИКНУЛИ, а не в центр камеры. При pointer-lock курсор в + // центре экрана → используем прицел камеры (aim не задаём). + if (document.pointerLockElement !== canvas) { + const rect = canvas.getBoundingClientRect(); + const cx = (e.clientX != null ? e.clientX : 0) - rect.left; + const cy = (e.clientY != null ? e.clientY : 0) - rect.top; + if (cx >= 0 && cy >= 0 && cx <= rect.width && cy <= rect.height) { + this.setAimScreenPoint(cx * (canvas.width / rect.width), + cy * (canvas.height / rect.height)); + } + } this._mouseDown = true; this._tryFire(); }; @@ -97,14 +109,28 @@ export class WeaponSystem { if (e.button !== 0) return; this._mouseDown = false; }; + // При свободном курсоре (3-е лицо) запоминаем позицию мыши — чтобы + // авто-огонь при удержании ЛКМ продолжал стрелять в точку курсора. + const onMove = (e) => { + if (!this._mouseDown) return; + if (document.pointerLockElement === canvas) return; + const rect = canvas.getBoundingClientRect(); + const cx = (e.clientX != null ? e.clientX : 0) - rect.left; + const cy = (e.clientY != null ? e.clientY : 0) - rect.top; + if (cx >= 0 && cy >= 0 && cx <= rect.width && cy <= rect.height) { + this._holdAim = { x: cx * (canvas.width / rect.width), y: cy * (canvas.height / rect.height) }; + } + }; const onKey = (e) => { if (e.code === 'KeyR') this.reload(); }; canvas.addEventListener('mousedown', onDown); window.addEventListener('mouseup', onUp); + window.addEventListener('mousemove', onMove); window.addEventListener('keydown', onKey); this._listeners.push({ target: canvas, type: 'mousedown', fn: onDown }); this._listeners.push({ target: window, type: 'mouseup', fn: onUp }); + this._listeners.push({ target: window, type: 'mousemove', fn: onMove }); this._listeners.push({ target: window, type: 'keydown', fn: onKey }); // Регистрируем перед-кадровый хук для авто-стрельбы (если auto=true) @@ -583,7 +609,12 @@ export class WeaponSystem { // (для tap-to-shoot на мобиле). Точка применяется один раз. let hit = null; let ray; - const aim = this._aimScreenPoint; + // aim: разовый клик (_aimScreenPoint) или удержание по курсору (_holdAim, + // только когда курсор свободен — нет pointer-lock). + let aim = this._aimScreenPoint; + if (!aim && this._holdAim && document.pointerLockElement !== this.scene3d?.canvas) { + aim = this._holdAim; + } try { if (aim) { ray = this.scene.createPickingRay(aim.x, aim.y, null, camera);