Newer
Older
rpg / docs / engine / art-pipeline.md

Арт-пайплайн: генерация пиксель-арта из кода

Арт игры генерируется кодом: сценарий apps/game/tools/pixelart/gen.mjs рисует спрайты на канвасе из движка и кодирует PNG без сторонних зависимостей (node:zlib + свой CRC32). Позже генератор можно заменить на рисованный арт — движку всё равно, откуда текстуры.

Файлы

Файл Что делает
packages/engine/tools/png.mjs PNG-кодек без зависимостей: encodePng/decodePng (RGBA 8 бит)
packages/engine/tools/canvas.mjs Canvas (таблица цветов — параметр): set/get пикселя, fillDiamond (изо-ромб), fromAscii, seeded rng
packages/engine/tools/imaging.mjs Кроп по альфе, nearest-масштаб, квантизация в палитру, обзорный лист; findFigures/resizeRect — фигуры на однотонном фоне
packages/engine/tools/atlas.mjs buildAtlas — кадры в строку + Pixi Spritesheet JSON
apps/game/tools/pixelart/palette.mjs Палитра игры из 32 цветов (hex + RGBA) — сценарный слой
apps/game/tools/pixelart/gen.mjs Генерация всех спрайтов игры → apps/game/assets/ (канвас с палитрой игры)

Запуск

npm run art     # node apps/game/tools/pixelart/gen.mjs — перегенерировать все PNG

Выходные файлы кладутся в apps/game/assets/ и грузятся в игре по ключам (tiles/grass, chars/hero_down_1, ...). Список ключей — в apps/game/src/Game.ts.

Палитра и правила

  • 32 цвета, тёмная холодная база; тёплые цвета — только живое (герой, NPC, колокольчики, огонь). Это правило сеттинга «Пепельных лугов» — см. docs/art-style.md.
  • Размеры: тайл 32×16 (изометрия 2:1), персонаж ~16×24, высокое дерево 32×48.
  • Пиксель перо = 1 виртуальный пиксель; никаких сглаженных краёв — контуры ступенчатые.

ASCII-спрайты

Персонажи задаются ASCII-картами (символ → цвет палитры) — читается и правится прямо в коде:

const heroDown = fromAscii(`
    ....0000....
    ...011111...
    ...
`, { '0': 'P4', '1': 'F2' });

Процедурные тайлы

Тайлы земли рисуются шумом (seeded rng) + ромбы fillDiamond (свет — верхние склоны, тень — нижние). Дерево: «облачная» крона из перекрывающихся окружностей, ствол, свисающий кабель — детали сеттинга.

Атласы

Если спрайтов много (анимации), генератор может собрать атлас: кадры в один PNG + Spritesheet JSON. Формат Pixi:

const atlas = {
    frames: {
        hero_walk_1: { frame: { x: 0, y: 0, w: 16, h: 24 } },
        hero_walk_2: { frame: { x: 16, y: 0, w: 16, h: 24 } }
    },
    meta: { image: 'hero_sheet.png', size: { w: 32, h: 24 }, scale: 1 }
};

Загрузка в игре — assets.loadAtlas('chars/hero_sheet'), кадры — assets.frames(key, 'hero_walk').

Сборка атласа из готовых кадров — buildAtlas(frames, names, { frameW, frameH, image }) из @rpg/engine/tools/atlas.mjs{ png, json } (формат выше).

Фигуры на однотонном фоне

Для пост-обработки AI-листов (несколько фигур на белом фоне) — imaging.mjs:

  • findFigures(img, { bg = 'corner', dist = 60, maxCount = 4, minArea = 400 }) — bbox'ы связных компонент пикселей, отстоящих от фона ('corner' — угловой пиксель) дальше dist; сортировка сверху-вниз/слева-направо.
  • resizeRect(img, rect, w, h, { bg, dist }) — nearest-даунскейл прямоугольника до w×h; пиксели ближе dist к фону становятся прозрачными (квантизация — отдельный шаг quantize с палитрой-параметром).

Сценарии применения (манифест персонажей, gen/build/promote, ремапер recolor.mjs, линтер lint-art.mjs) — в игре: apps/game/tools/aiart/ (см. docs/plan.md, работа B).

Замена генератора на рисованный арт

Движку не важно происхождение текстур. Чтобы перейти на рисованный арт:

  1. Рисуете PNG того же размера (или атлас + JSON) в apps/game/assets/.
  2. Ключи ассетов не меняются — код игры не трогается.
  3. Палитру соблюдаете, чтобы арт не выбивался из сеттинга (docs/art-style.md).

Aseprite/Piskel: экспорт в PNG с теми же именами файлов, что даёт генератор.