Files

63 lines
10 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"bytes"
"image/png"
"path/filepath"
"aego/art"
)
type lesson struct{ ID, EN, RU, ENText, RUText, ENAction, RUAction, Check string }
var lessons = []lesson{
{"first-sprite", "Draw your first sprite", "Первый спрайт", "Create a 32×32 drawing. Choose Pencil and draw a short line. Fill a closed shape, then undo and redo.", "Создайте рисунок 32×32. Выберите карандаш, нарисуйте линию и замкнутую фигуру, залейте её. Проверьте отмену и повтор.", "Draw on the canvas; compare with the starting pixels.", "Нарисуйте на холсте; сравните с исходными пикселями.", "pixels"},
{"layers", "Layers and transparency", "Слои и прозрачность", "Add a raster layer with + in Layers. Paint on it, change opacity, hide it and restore visibility. Do not flatten the source to save it.", "Добавьте растровый слой кнопкой +. Нарисуйте на нём, измените непрозрачность, скройте и верните видимость. Для сохранения исходника сведение не требуется.", "Add at least one layer.", "Добавьте хотя бы один слой.", "layers"},
{"pixel-perfect", "Pixel perfect and dithering", "Pixel Perfect и дизеринг", "Turn Pixel Perfect on beside the canvas. Draw a stepped curve. Set Dither to 8, draw again and compare. The stroke remains a single Undo operation.", "Включите Pixel Perfect у холста и проведите ступенчатую линию. Установите дизеринг 8 и сравните результат. Штрих остаётся одной операцией отмены.", "Change the drawing with the selected tool.", "Измените рисунок выбранным инструментом.", "pixels"},
{"indexed", "Palette and Indexed Color", "Палитра и индексированные цвета", "This document uses indexed pixels. Select a palette entry to draw. Palette Order preserves appearance; explicit Remap intentionally changes the image. Save the editable source to keep indices.", "В этом документе пиксели хранят индексы палитры. Выберите цвет и рисуйте. «Порядок» сохраняет внешний вид; явный Remap перекрашивает изображение. Сохраните исходник, чтобы сохранить индексы.", "Draw with a palette index.", "Нарисуйте цветом из палитры.", "pixels"},
{"animation", "Frame-by-frame animation", "Покадровая анимация", "Use + Frame or Duplicate, then edit a frame and set its duration in milliseconds. Enter starts playback. A sheet export contains PNG plus frame metadata; it is not the native .aeanim export.", "Используйте «+ Кадр» или «Копия», измените рисунок и задайте длительность в миллисекундах. Enter запускает просмотр. Веб-экспорт спрайт-листа содержит PNG и метаданные, а не готовый .aeanim.", "Add a frame to the starting animation.", "Добавьте кадр к исходной анимации.", "frames"},
{"linked-cels", "Linked cels", "Связанные ячейки", "Select a drawn frame and click Link. Paint on the linked image: both cels change. Unlink before an independent edit. Duplicate makes an independent copy.", "Выберите кадр с рисунком и нажмите «Связать». Изменение общего изображения видно в обеих ячейках. Для независимой правки используйте «Отвязать». «Копия» создаёт независимое изображение.", "Create another cel linked to an existing image.", "Создайте ячейку, связанную с существующим изображением.", "linked"},
{"onion-skin", "Onion skin", "Калька", "Enable Onion Skin near playback and select an intermediate frame. Paint between the previous and next outlines. Onion skin is a presentation overlay and is not exported into your pixels.", "Включите «Калька» рядом с воспроизведением и выберите промежуточный кадр. Рисуйте между контурами соседних кадров. Калька не включается в экспортируемые пиксели.", "Turn on Onion Skin.", "Включите кальку.", "onion"},
{"character", "Character animation", "Анимация персонажа", "Edit an eight-frame character loop. Keep the feet aligned and change durations. This web app previews frames; it does not run the project's Animator or change a collider.", "Отредактируйте восьмикадровый цикл. Следите за положением ног и меняйте длительности. Веб-приложение показывает кадры, но не запускает Animator проекта и не меняет коллайдер.", "Edit a frame and retain at least eight frames.", "Измените рисунок, сохранив минимум восемь кадров.", "character"},
{"surface-channels", "Emission and normal channels", "Emission и normal-каналы", "Choose Emission or Normal above the canvas and paint channel pixels. In Preview choose Lit / Normal, move the Light slider and enable Bloom. Normal vectors are authored as RGB data; they are not inferred from albedo. Height is displayed as a data channel; parallax is not implemented.", "Выберите Emission или Normal у холста и нарисуйте данные канала. В «Предпросмотре» выберите Lit / Normal, переместите свет и включите Bloom. RGB нормали задаются явно, а не вычисляются из albedo. Height показывается как канал данных; parallax не реализован.", "Create or modify an emission/normal cel.", "Создайте или измените ячейку emission/normal.", "channels"},
{"explosion", "Explosion flipbook", "Покадровый взрыв", "Draw the expansion and fading of an explosion. Preview the eight frames and export a sheet. Choose ParticleSystem2D in Preview or the VFX workspace. Change the emission rate and enable Bloom. This runs the same CPU particle backend as Aego; assignment to a project object is available in native Aego.", "Нарисуйте расширение и затухание взрыва, просмотрите восемь кадров и экспортируйте спрайт-лист. Выберите ParticleSystem2D в предпросмотре или рабочее пространство VFX. Меняйте число частиц в секунду и включайте Bloom. Работает тот же CPU backend Aego; назначение объекту проекта выполняется в native Aego.", "Edit the flipbook while retaining eight frames.", "Измените flipbook, сохранив восемь кадров.", "character"},
{"aseprite", "Import Aseprite", "Импорт Aseprite", "Use Open to select your .ase/.aseprite. Supported raster, linked cel, layer and tag data is imported by Go; unsupported chunks produce diagnostics. There is no lossless Aseprite round-trip. Download the original point4frames.aseprite fixture linked below, open it locally, and inspect all four frames; its license and provenance are included.", "Откройте собственный .ase/.aseprite. Go-импортёр читает поддерживаемые растровые данные, связанные ячейки, слои и метки; неподдерживаемые chunks вызывают диагностику. Lossless round-trip не заявляется. Скачайте оригинальный point4frames.aseprite по ссылке ниже, откройте его и проверьте четыре кадра; лицензия и происхождение включены.", "Import your Aseprite file; visually inspect diagnostics and frames.", "Импортируйте собственный файл Aseprite и проверьте диагностику и кадры.", "manual"},
{"export", "Export and use in Aego", "Экспорт и использование в Aego", "Save .aeart, export PNG and a sprite sheet. Reopen the .aeart to continue editing. Native Aego must have the Stage 12.5 importer installed; game assignment and .aeanim are not performed by this standalone app.", "Сохраните .aeart, экспортируйте PNG и спрайт-лист. Снова откройте .aeart для редактирования. В native Aego требуется импортёр Stage 12.5; standalone-приложение не назначает игровой объект и не создаёт .aeanim.", "Download the editable source and reopen it.", "Скачайте редактируемый исходник и откройте его заново.", "manual"},
}
// Checked-in fixtures are decoded with the real Core and rendered here. Keeping
// their stable document IDs makes repeated builds from identical inputs byte-identical.
func writeExamples(out, fixtures string) ([]example, error) {
var result []example
for _, l := range lessons {
b, e := readBounded(filepath.Join(fixtures, l.ID+".aeart"), maxFileBytes)
if e != nil {
return nil, e
}
d, e := art.Decode(b, art.Limits{})
if e != nil {
return nil, e
}
im, e := d.Render(d.Info().Frames[0].ID, art.RenderOptions{})
d.Close()
if e != nil {
return nil, e
}
var thumbnail bytes.Buffer
if e = png.Encode(&thumbnail, im); e != nil {
return nil, e
}
file := "examples/" + l.ID + "/start.aeart"
thumb := "examples/" + l.ID + "/preview.png"
if e = writeNew(filepath.Join(out, filepath.FromSlash(file)), b); e != nil {
return nil, e
}
if e = writeNew(filepath.Join(out, filepath.FromSlash(thumb)), thumbnail.Bytes()); e != nil {
return nil, e
}
result = append(result, example{l.ID, 1, map[string]string{"en": l.EN, "ru": l.RU}, map[string]string{"en": l.ENText, "ru": l.RUText}, file, thumb, digest(b), len(b)})
}
return result, nil
}