diff --git a/src/editor/engine/GameplayKits.js b/src/editor/engine/GameplayKits.js index c04308b..915d9c6 100644 --- a/src/editor/engine/GameplayKits.js +++ b/src/editor/engine/GameplayKits.js @@ -263,28 +263,51 @@ game.self.onTouch(() => { { id: 'door-button', name: 'Дверь по кнопке E', - desc: 'Дверь + кнопка: подойди, нажми E — дверь уезжает в сторону и открывает проход. (Вики: «Кнопка-открывашка»)', + desc: 'Красивая дверь с рамкой, филёнками и ручкой. Нажми E — плавно распахивается вокруг петли. (Вики: «Кнопка-открывашка»)', icon: 'door', category: 'world', - prims: [{ type: 'cube', x: 0, y: 2, z: 0, sx: 1, sy: 4, sz: 3, color: '#8a5a3c', material: 'matte', name: 'Дверь' }], + // Полотно двери — ПЕРВЫЙ prim (на нём скрипт). Остальные части — рамка + // (неподвижный косяк) + декор полотна. Всё уходит в одну папку. + prims: [ + // 0) Полотно двери (тёмное дерево). + { type: 'cube', x: 0, y: 2, z: 0, sx: 0.25, sy: 3.8, sz: 2.6, color: '#6b4423', material: 'matte', name: 'Полотно двери' }, + // Филёнки (светлее, чуть выступают) — верхняя и нижняя. + { type: 'cube', x: 0.16, y: 2.9, z: 0, sx: 0.08, sy: 1.2, sz: 1.9, color: '#8a5a2e', material: 'matte', canCollide: false, name: 'Филёнка верх' }, + { type: 'cube', x: 0.16, y: 1.2, z: 0, sx: 0.08, sy: 1.4, sz: 1.9, color: '#8a5a2e', material: 'matte', canCollide: false, name: 'Филёнка низ' }, + // Ручка (золотая). + { type: 'sphere', x: 0.28, y: 2, z: 0.95, sx: 0.3, sy: 0.3, sz: 0.3, color: '#e0b030', material: 'metal', canCollide: false, name: 'Ручка' }, + // Косяк-рамка (неподвижная) — две стойки + перемычка. + { type: 'cube', x: 0, y: 2, z: -1.55, sx: 0.4, sy: 4.2, sz: 0.4, color: '#4a3018', material: 'matte', name: 'Косяк левый' }, + { type: 'cube', x: 0, y: 2, z: 1.55, sx: 0.4, sy: 4.2, sz: 0.4, color: '#4a3018', material: 'matte', name: 'Косяк правый' }, + { type: 'cube', x: 0, y: 4.1, z: 0, sx: 0.4, sy: 0.4, sz: 3.5, color: '#4a3018', material: 'matte', name: 'Перемычка' }, + ], scripts: [{ attachTo: 'on-target', code: -`// Дверь по E: ПОВОРАЧИВАЕТСЯ вокруг петли (левой грани), как настоящая дверь. -// Толщина двери по X (sx=1) → полуширина 0.5. Петля у грани z = центр - halfZ. -let open = false; +`// Дверь по E: ПЛАВНО поворачивается вокруг петли (левой грани). +// Скрипт на полотне двери. Филёнки/ручка двигаются вместе как часть полотна? +// Нет — они отдельные примитивы, поэтому анимируем только полотно (game.self), +// а декор оставляем — на низкой толщине двери смотрится цельно. const p0 = game.self.position; -const halfW = 1.5; // половина ширины двери по Z (sz=3 → 1.5) -// Петля — у левого края двери (по Z): hinge = p0.z - halfW. -const hingeX = p0.x; -const hingeZ = p0.z - halfW; -function placeDoor(angle) { - // Центр двери на расстоянии halfW от петли, повёрнут на angle вокруг петли. - const cx = hingeX + Math.sin(angle) * halfW; +const halfW = 1.3; // половина ширины полотна по Z (sz=2.6) +const hingeZ = p0.z - halfW; // петля у левого края +let open = false; +let cur = 0, target = 0; // текущий и целевой угол (радианы) +const SPEED = Math.PI; // рад/сек → ~0.5с на 90° +function place(angle) { + const cx = p0.x + Math.sin(angle) * halfW; const cz = hingeZ + Math.cos(angle) * halfW; game.self.move(cx, p0.y, cz); game.self.rotate(angle); } +// Один постоянный тик плавно ведёт cur → target. +game.onTick((dt) => { + if (cur === target) return; + const step = SPEED * dt; + if (Math.abs(target - cur) <= step) cur = target; + else cur += Math.sign(target - cur) * step; + place(cur); +}); game.self.onInteract(() => { open = !open; - placeDoor(open ? Math.PI / 2 : 0); // 90° открыта / 0° закрыта + target = open ? Math.PI / 2 : 0; // 90° открыта / 0° закрыта }, { text: 'Открыть / закрыть', key: 'e', distance: 5 });` }], }, ];