From 045f892aaa67f5400ec8d08feabd359f81be8aed Mon Sep 17 00:00:00 2001 From: min Date: Fri, 5 Jun 2026 19:13:02 +0300 Subject: [PATCH] =?UTF-8?q?fix(studio):=20=D1=81=D0=B2=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D1=84=D0=BE=D1=80=20(obj.color=20=D0=BF=D0=BE=20ref),=20=D0=B3?= =?UTF-8?q?=D1=80=D1=8F=D0=B4=D0=BA=D0=B0=20=D1=80=D0=B0=D1=81=D1=82=D1=91?= =?UTF-8?q?=D1=82+=D0=B7=D1=80=D0=B5=D0=B5=D1=82=20(obj.scale),=20=D0=BA?= =?UTF-8?q?=D0=B8=D1=82=20=D1=81=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D1=8F=20HP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) scene.setColor теперь принимает {ref} (obj.color=hex), не только {id}. Светофор переключал цвета через obj.color, но ref игнорировался → не работал. 2) Грядка: добавлен obj.scale (scene.setScale → mesh.scaling). Урожай после сбора исчезает, растёт за 5с (scale 0→1) и зреет цветом красный→зелёный, при полном размере снова собирается. 3) Кит «HP-бар» теперь сам прячет стандартный HUD HP (setHpVisible false). Новый кит «Скрыть стандартный HUD HP» — отдельно прячет дефолтную полосу. Co-Authored-By: Claude Opus 4.8 --- src/editor/engine/GameRuntime.js | 20 +++++++++-- src/editor/engine/GameplayKits.js | 44 +++++++++++++++++++----- src/editor/engine/ScriptSandboxWorker.js | 6 ++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/editor/engine/GameRuntime.js b/src/editor/engine/GameRuntime.js index efb6a72..1280a55 100644 --- a/src/editor/engine/GameRuntime.js +++ b/src/editor/engine/GameRuntime.js @@ -2904,13 +2904,29 @@ export class GameRuntime { return; } + if (cmd === 'scene.setScale') { + try { + const k = Number(payload?.scale); + if (!Number.isFinite(k) || k < 0) return; + const pm = this.scene3d?.primitiveManager; + const rid = this._resolvePrimitiveId(payload?.id ?? payload?.ref); + const data = (pm && rid != null) ? pm.instances.get(rid) : null; + if (data?.mesh) { + if (data._worldMatrixFrozen) { try { data.mesh.unfreezeWorldMatrix?.(); } catch (e) {} data._worldMatrixFrozen = false; } + data.mesh.scaling.set(k, k, k); // визуальный масштаб от исходного размера + } + } catch (e) {} + return; + } + if (cmd === 'scene.setColor') { try { const color = payload?.color; if (typeof color !== 'string') return; // Окрашиваемый блок (studs-block): ref вида 'block:x,y,z' → // меняем per-instance цвет через BlockManager.setBlockColor. - const ref = payload?.id; + // ВАЖНО: obj.color=hex шлёт {ref}, а self.setColor — {id}. Берём оба. + const ref = payload?.id ?? payload?.ref; if (typeof ref === 'string' && ref.startsWith('block:')) { const parts = ref.slice(6).split(',').map(Number); if (parts.length === 3 && parts.every(Number.isFinite)) { @@ -2920,7 +2936,7 @@ export class GameRuntime { } const pm = this.scene3d?.primitiveManager; if (!pm) return; - const rid = this._resolvePrimitiveId(payload?.id); + const rid = this._resolvePrimitiveId(payload?.id ?? payload?.ref); const data = rid != null ? pm.instances.get(rid) : null; if (data) { data.color = color; diff --git a/src/editor/engine/GameplayKits.js b/src/editor/engine/GameplayKits.js index 4d5086d..05b8fc9 100644 --- a/src/editor/engine/GameplayKits.js +++ b/src/editor/engine/GameplayKits.js @@ -494,17 +494,35 @@ game.every(2, () => { p = (p+1) % phases.length; show(); });` }], { type: 'sphere', x: 0, y: 0.8, z: 0, sx: 1, sy: 1, sz: 1, color: '#3f9a48', material: 'matte', name: 'Урожай' }, ], scripts: [{ attachTo: 'on-target', code: -`// Грядка: урожай растёт, по взаимодействию — сбор +10 монет. +`// Грядка: собрал урожай → он исчезает, растёт заново (цвет красный→зелёный), +// при полном размере снова созрел и его можно собрать. const plant = game.scene.findOne('Урожай'); -let ripe = true; +let ripe = true; // созрел ли (можно собирать) +let growth = 1; // 0..1 — степень роста +// Плавная интерполяция цвета красный(незрелый)→зелёный(спелый). +function colorAt(g) { + const r = Math.round(0xb0 + (0x3f - 0xb0) * g); + const gr = Math.round(0x40 + (0x9a - 0x40) * g); + const b = Math.round(0x2e + (0x48 - 0x2e) * g); + return '#' + [r,gr,b].map(v => v.toString(16).padStart(2,'0')).join(''); +} game.self.onInteract(() => { if (!ripe) { game.ui.set('h','Ещё не созрело...', {x:50,y:80,anchor:'bottom',color:'#bbb',size:16}); return; } ripe = false; + growth = 0; game.broadcast('coins', { add: 10 }); - if (plant) plant.visible = false; + if (plant) { plant.scale = 0.01; plant.color = colorAt(0); } game.ui.set('h','🌾 Собрано! +10 монет', {x:50,y:80,anchor:'bottom',color:'#ffd23a',size:18}); - game.after(3, () => { if (plant) plant.visible = true; ripe = true; game.ui.set('h',''); }); -}, { text: 'Собрать урожай', key: 'e', distance: 4 });` }], + game.after(2, () => game.ui.set('h','')); +}, { text: 'Собрать урожай', key: 'e', distance: 4 }); +// Рост: за ~5 секунд от 0 до 1. +game.onTick((dt) => { + if (ripe || !plant) return; + growth = Math.min(1, growth + dt / 5); + plant.scale = Math.max(0.05, growth); + plant.color = colorAt(growth); + if (growth >= 1) ripe = true; +});` }], }, { id: 'falling-objects', @@ -541,11 +559,21 @@ game.onMessage('score', (m) => { score += (m && m.add) ? m.add : 1; show(); });` desc: 'Показывает HP игрока в углу экрана, обновляется при уроне/лечении. (Вики: «Лава-пол»)', icon: 'warning', category: 'ui', scripts: [{ attachTo: 'global', code: -`// HP-индикатор игрока в HUD. +`// Своя полоска HP. Сначала прячем стандартную, чтобы не дублировалась. +game.hud.setHpVisible(false); function show(){ const hp = Math.max(0, Math.round(game.player.hp)); - game.ui.set('hp', '❤ ' + hp, { x:8, y:12, anchor:'top', color: hp>30?'#36d57a':'#ff4444', size:22 }); } + game.ui.set('hp', '❤ ' + hp + ' / 100', { x:50, y:94, anchor:'bottom', color: hp>30?'#36d57a':'#ff4444', size:22 }); } show(); -game.every(0.3, show);` }], +game.every(0.2, show);` }], + }, + { + id: 'hide-default-hp', + name: 'Скрыть стандартный HUD HP', + desc: 'Прячет стандартную полосу здоровья сверху — чтобы показать свою. (Свойство игрока game.hud.setHpVisible)', + icon: 'warning', category: 'ui', + scripts: [{ attachTo: 'global', code: +`// Скрыть стандартную полосу здоровья игрока. +game.hud.setHpVisible(false);` }], }, { id: 'code-door', diff --git a/src/editor/engine/ScriptSandboxWorker.js b/src/editor/engine/ScriptSandboxWorker.js index 5bad044..433dc78 100644 --- a/src/editor/engine/ScriptSandboxWorker.js +++ b/src/editor/engine/ScriptSandboxWorker.js @@ -554,6 +554,12 @@ function _getOrCreateInstance(ref, kindHint) { _send('scene.setColor', { ref, color: String(value) }); return true; } + if (prop === 'scale') { + // Равномерный визуальный масштаб объекта (1 = исходный размер). + const k = Number(value); + if (Number.isFinite(k) && k >= 0) _send('scene.setScale', { ref, scale: k }); + return true; + } if (prop === 'transparency' || prop === 'opacity') { const v = Number(value); if (Number.isFinite(v)) {