feat(studio): красивая дверь (рамка+филёнки+ручка) + плавная анимация открытия

- Дверь теперь многочастная: полотно из тёмного дерева + 2 филёнки + золотая
  ручка + косяк-рамка (2 стойки + перемычка). Уходит в общую папку.
- Плавное открытие: постоянный onTick ведёт угол cur→target со скоростью
  ~0.5с на 90° (вместо мгновенного скачка). Поворот вокруг петли сохранён.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
min 2026-06-05 09:16:46 +03:00
parent 32cbb7bbe9
commit 9a58c34303

View File

@ -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 });` }],
},
];