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',
|
||||
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 });` }],
|
||||
},
|
||||
];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user