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:
parent
32cbb7bbe9
commit
9a58c34303
@ -263,28 +263,51 @@ game.self.onTouch(() => {
|
|||||||
{
|
{
|
||||||
id: 'door-button',
|
id: 'door-button',
|
||||||
name: 'Дверь по кнопке E',
|
name: 'Дверь по кнопке E',
|
||||||
desc: 'Дверь + кнопка: подойди, нажми E — дверь уезжает в сторону и открывает проход. (Вики: «Кнопка-открывашка»)',
|
desc: 'Красивая дверь с рамкой, филёнками и ручкой. Нажми E — плавно распахивается вокруг петли. (Вики: «Кнопка-открывашка»)',
|
||||||
icon: 'door', category: 'world',
|
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:
|
scripts: [{ attachTo: 'on-target', code:
|
||||||
`// Дверь по E: ПОВОРАЧИВАЕТСЯ вокруг петли (левой грани), как настоящая дверь.
|
`// Дверь по E: ПЛАВНО поворачивается вокруг петли (левой грани).
|
||||||
// Толщина двери по X (sx=1) → полуширина 0.5. Петля у грани z = центр - halfZ.
|
// Скрипт на полотне двери. Филёнки/ручка двигаются вместе как часть полотна?
|
||||||
let open = false;
|
// Нет — они отдельные примитивы, поэтому анимируем только полотно (game.self),
|
||||||
|
// а декор оставляем — на низкой толщине двери смотрится цельно.
|
||||||
const p0 = game.self.position;
|
const p0 = game.self.position;
|
||||||
const halfW = 1.5; // половина ширины двери по Z (sz=3 → 1.5)
|
const halfW = 1.3; // половина ширины полотна по Z (sz=2.6)
|
||||||
// Петля — у левого края двери (по Z): hinge = p0.z - halfW.
|
const hingeZ = p0.z - halfW; // петля у левого края
|
||||||
const hingeX = p0.x;
|
let open = false;
|
||||||
const hingeZ = p0.z - halfW;
|
let cur = 0, target = 0; // текущий и целевой угол (радианы)
|
||||||
function placeDoor(angle) {
|
const SPEED = Math.PI; // рад/сек → ~0.5с на 90°
|
||||||
// Центр двери на расстоянии halfW от петли, повёрнут на angle вокруг петли.
|
function place(angle) {
|
||||||
const cx = hingeX + Math.sin(angle) * halfW;
|
const cx = p0.x + Math.sin(angle) * halfW;
|
||||||
const cz = hingeZ + Math.cos(angle) * halfW;
|
const cz = hingeZ + Math.cos(angle) * halfW;
|
||||||
game.self.move(cx, p0.y, cz);
|
game.self.move(cx, p0.y, cz);
|
||||||
game.self.rotate(angle);
|
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(() => {
|
game.self.onInteract(() => {
|
||||||
open = !open;
|
open = !open;
|
||||||
placeDoor(open ? Math.PI / 2 : 0); // 90° открыта / 0° закрыта
|
target = open ? Math.PI / 2 : 0; // 90° открыта / 0° закрыта
|
||||||
}, { text: 'Открыть / закрыть', key: 'e', distance: 5 });` }],
|
}, { text: 'Открыть / закрыть', key: 'e', distance: 5 });` }],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user