set(key, v)}
min={min}
step={step}
/>
);
const bool = (key: string, label: string, fallback = false) => (
);
const select = (
key: string,
label: string,
options: [string, string][],
fallback = "",
) => (
);
const pair = (
key: string,
label: string,
fallback = [0, 0],
min?: number,
) => (
{[0, 1].map((i) => (
{
const a = [...(c[key] || fallback)];
a[i] = v;
set(key, a);
}}
/>
))}
);
const asset = project.assets.find((a) => a.id === c.assetId),
frames = asset?.image?.frames || [];
const imageSelect = (
<>
{asset && (
)}
>
);
const sorting = (
<>
{num("layer", "Слой", 0, undefined, 1)}
{num("order", "Порядок в слое", 0, undefined, 1)}
{bool("sortY", "Сортировать по Y")}
{num("alpha", "Непрозрачность", 1, 0)}
{bool("lit", "Освещение 3D")}
>
);
if (type === "sprite")
return (
<>
{imageSelect}
{pair(
"size",
"Размер",
frames[c.frame || 0]
? [
frames[c.frame || 0].width / asset!.image!.pixelsPerUnit,
frames[c.frame || 0].height / asset!.image!.pixelsPerUnit,
]
: [1, 1],
0.001,
)}
{c.size && (
)}
{bool("flipX", "Отразить X")}
{bool("flipY", "Отразить Y")}
{sorting}
>
);
if (type === "tilemap")
return (
<>
{imageSelect}
{pair("tileSize", "Тайл", [1, 1], 0.001)}
{num("width", "Столбцы", 32, 1, 1)}
{num("height", "Строки", 18, 1, 1)}
{bool("collisions", "Коллизии тайлов")}
{sorting}
Рисование в виде 2D. Один мазок — один шаг Undo. Начало карты: нижний
левый угол.
{[
["brush", "Кисть"],
["fill", "Заливка"],
["erase", "Ластик"],
].map(([v, l]) => (
))}
{(frames.length ? frames : [{ name: "Квадрат" }]).map((f, i) => (
))}
{c.cells.length} тайлов. Выберите инструмент Q/W/E/R, чтобы закончить
рисование.
>
);
if (type === "spriteAnimator") {
const clips = c.clips || [];
return (
<>
{select("autoplay", "Автозапуск", [
["", "Нет"],
...clips.map((cl: any) => [cl.name, cl.name] as [string, string]),
])}
{bool("autoStates", "Авто: idle / run / jump")}
Автоматические состояния используют скорость и grounded из
Character2D.
{clips.map((cl: any, i: number) => {
const update = (v: any) =>
set(
"clips",
clips.map((old: any, j: number) =>
j === i ? { ...old, ...v } : old,
),
);
return (
update({ fps })}
/>
);
})}
>
);
}
if (type === "rigidbody2d")
return (
<>
{select(
"type",
"Тип тела",
[
["fixed", "Неподвижное"],
["dynamic", "Динамическое"],
["kinematic", "Кинематическое"],
],
"dynamic",
)}
{num("mass", "Масса", 1, 0)}
{num("friction", "Трение", 0.5, 0)}
{num("restitution", "Упругость", 0, 0)}
{num("gravityScale", "Множитель гравитации", 1)}
{num("linearDamping", "Сопротивление", 0, 0)}
{num("angularDamping", "Угловое сопротивление", 0, 0)}
{bool("lockRotation", "Фиксировать вращение", true)}
{bool("ccd", "Непрерывные коллизии", true)}
>
);
if (type === "collider2d")
return (
<>
{c.shape === "box" && pair("size", "Размер", [1, 1], 0.001)}
{["circle", "capsule"].includes(c.shape) &&
num("radius", "Радиус", 0.5, 0.001)}
{c.shape === "capsule" && num("height", "Высота", 1.8, 0.001)}
{c.shape === "polygon" && (
<>
Вершины выпуклого контура по порядку.
{c.points.map((p: number[], i: number) => (
{[0, 1].map((j) => (
set(
"points",
c.points.map((old: number[], k: number) =>
k === i ? old.map((x, l) => (l === j ? v : x)) : old,
),
)
}
/>
))}
{c.points.length > 3 && (
)}
))}
>
)}
{pair("offset", "Смещение")}
{bool("sensor", "Триггер")}
{bool("oneWay", "Односторонняя платформа")}
{num("membership", "Группа (битовая маска)", 1, 0, 1)}
{num("mask", "Взаимодействует с", 65535, 0, 1)}
>
);
if (type === "character2d")
return (
<>
{select(
"mode",
"Игра",
[
["platformer", "Платформер"],
["topDown", "Вид сверху"],
],
"platformer",
)}
{bool("controls", "WASD / стрелки / касание", true)}
{num("speed", "Скорость", 5, 0)}
{c.mode !== "topDown" && (
<>
{num("jumpSpeed", "Скорость прыжка", 8, 0)}
{num("gravity", "Гравитация персонажа", 20, 0)}
{num("autostep", "Высота ступени", 0.2, 0)}
>
)}
Прыжок: пробел. При собственном скрипте отключите встроенное
управление.
>
);
if (type === "joint2d")
return (
<>
{select(
"type",
"Соединение",
[
["revolute", "Шарнир"],
["fixed", "Жёсткое"],
["rope", "Верёвка"],
["spring", "Пружина"],
],
"revolute",
)}
{select(
"targetId",
"Второе тело",
project.scenes
.find((s) => s.id === project.activeSceneId)!
.entities.filter(
(n) => n.id !== node.id && n.components.rigidbody2d,
)
.map((n) => [n.id, n.name]),
)}
{pair("anchor", "Крепление")}
{pair("targetAnchor", "Крепление цели")}
{["rope", "spring"].includes(c.type) &&
num("length", "Длина", 1, 0.001)}
{c.type === "spring" && (
<>
{num("stiffness", "Жёсткость", 50, 0)}
{num("damping", "Затухание", 5, 0)}
>
)}
>
);
if (type === "camera2d")
return (
<>
{num("orthoWidth", "Ширина кадра", 20, 0.1)}
{bool("pixelPerfect", "Pixel Perfect")}
{num("pixelsPerUnit", "Пикселей на единицу", 100, 0.1)}
{pair("offset", "Смещение", [0, 0])}
>
);
return null;
}