Содержание урока · 10 разделов
У игры есть спрайты и текст, но между ними дыра: полоса здоровья, рамка экрана, сетка в редакторе и круг перезарядки не тянут на полноценные ассеты — ради них не заводят ни атлас, ни художника. Именно эту дыру закрывает пакет vector из поставки Ebitengine: векторная графика примитивов рождается прямо в коде, меняется константой и весит ноль байт. В этом уроке мы соберём из линий и кругов боевой HUD, поймём, когда примитив честнее спрайта, посчитаем цену каждого вызова и прикрутим к примитивам хитбоксы — геометрию, которую игра проверяет так же легко, как рисует. К концу урока прототип сможет выглядеть собранным ещё до появления настоящего арта.
Пакет vector: готовые функции#
Пакет vector открывается пятью функциями быстрого рисования: FillCircle заливает круг, StrokeCircle обводит окружность, StrokeLine проводит линию заданной ширины, FillRect и StrokeRect заливают и обводят прямоугольники. Общая схема аргументов одинаковая: холст, координаты в float32, цвет и последний флаг antialias — сглаживание краёв. Включённый флаг даёт мягкие края ценой дополнительных вычислений, выключенный рисует резко и дешевле: для статичного UI сглаживание включают, для сотен объектов на кадр — выключают. Координаты — пиксели холста, и круг задаётся центром плюс радиусом, а не прямоугольником: геометрия круга живёт прямо в аргументах. Массовость — главный аргумент в пользу готовых функций: сетка из сотни линий или облако частиц из кругов собираются циклом без единой картинки, и правка цвета — одна константа в коде, а не перерисовка ассета.
Имена честно отражают историю пакета: DrawFilledCircle из старых статей с версии 2.9 помечена устаревшей и стала алиасом FillCircle — в новом коде используйте актуальные имена, чтобы не тащить предупреждения и не путать читателя. Цвет — обычный color.Color из image/color, и прозрачность через альфу работает так же, как у спрайтов: полупрозрачный прямоугольник поверх экрана — это FillRect с альфой, а не отдельная картинка затемнения. Именно так собирается оверлей паузы из урока про сцены: затемнение плюс надпись — и никакой картинки не понадобилось.
HUD: рамка, здоровье и кулдаун
GOfunc drawHUD(screen *ebiten.Image, g *Game) {
// рамка экрана: контур прямоугольника шириной 2
vector.StrokeRect(screen, 8, 8, 624, 464, 2,
color.RGBA{0x80, 0x80, 0x80, 0xff}, false)
// полоса здоровья: ширина зависит от hp
w := float32(g.hp) / float32(g.maxHP) * 120
vector.FillRect(screen, 16, 16, w, 14,
color.RGBA{0xff, 0x40, 0x40, 0xff}, false)
// контур индикатора перезарядки
vector.StrokeCircle(screen, 600, 40, 16, 2,
color.RGBA{0xff, 0xff, 0xff, 0xff}, false)
// заливка растёт по мере готовности
r := float32(12) * (1 - g.cooldown/g.cooldownMax)
vector.FillCircle(screen, 600, 40, r,
color.RGBA{0x40, 0x80, 0xff, 0xff}, true)
}Рамка — один вызов StrokeRect с шириной линии два: ни картинки, ни четырёх вручную поставленных полосок. Полоса здоровья — FillRect, чья ширина считается из отношения hp к максимуму: ноль здоровья даёт пустую полосу, полный запас — сто двадцать пикселей. Индикатор перезарядки собран из двух кругов: StrokeCircle рисует постоянный контур, а радиус заливки FillCircle растёт по мере готовности — от нуля до почти полного радиуса. Заливка со сглаживанием ради мягкого края, рамка без него: на статичном HUD лишние вычисления не нужны.
Какая функция рисует залитый круг в актуальном API пакета vector?
Path: фигуры из сегментов#
Когда готовых фигур мало, включается vector.Path — собираемая фигура. Метод MoveTo ставит перо в точку, LineTo тянет сегмент до следующей, Arc рисует дугу по центру, радиусу, стартовому и конечному углу, Close замыкает контур. Готовый путь отдаётся в FillPath — заливку — или StrokePath — обводку — вместе с опциями: StrokeOptions задаёт ширину линии Width и стили концов и стыков, DrawPathOptions — сглаживание AntiAlias и цвет через ColorScale. Путь — просто список инструкций: его можно строить заново каждый кадр, а можно собрать один раз и переиспользовать, и от этого выбора зависит производительность следующего раздела. Кроме линий у пера есть кривые: QuadTo тянет квадратичную кривую через одну контрольную точку, CubicTo — кубическую через две, и на них строятся плавные траектории и органические контуры; в играх курса хватает линий и дуг, но знать про кривые стоит уже потому, что они объясняют, почему дуги в Path сегментированы.
Круг из готовой функции и круг из Path — разные вещи по цене: FillCircle внутри строит путь с дугой на две сотни сегментов, но делает это в оптимизированном виде с переиспользованием вершин. Свой Path нужен там, где фигура нестандартна: дуга перезарядки по углу, ломаная трассы, многоугольник препятствия. Для прямых и прямоугольников Path избыточен — готовые функции короче, быстрее и читаются в одну строку. Ещё один плюс собранного пути — переносимость: один и тот же Path можно обвести на любом холсте — на экране, на offscreen-изображении для предрендера или на карте, куда смотрит камера, — геометрия не знает, куда её рисуют, и это делает пути удобными заготовками для слоёв интерфейса.
Кулдаун-дуга и линия трассы
GO// кулдаун-дуга: прогресс виден по углу
var ring vector.Path
start := float32(-math.Pi / 2) // старт сверху
end := start + 2*math.Pi*(1-g.cooldown/g.cooldownMax)
ring.Arc(600, 40, 16, start, end, vector.Clockwise)
vector.StrokePath(screen, &ring,
&vector.StrokeOptions{Width: 3},
&vector.DrawPathOptions{AntiAlias: true})
// линия трассы выстрела
vector.StrokeLine(screen, g.px, g.py, g.tx, g.ty, 2,
color.RGBA{0xff, 0xff, 0x00, 0xff}, true)Дуга перезарядки — Path с одним вызовом Arc: центр, радиус, стартовый угол сверху и конечный угол, привязанный к прогрессу, направление по часовой. StrokePath обводит путь линией шириной три со сглаживанием — получается полый индикатор, который отрастает по кругу без единого кадра анимации. Трасса выстрела — готовая StrokeLine: две точки, ширина два, жёлтый цвет. Путь ring строится заново каждый кадр, потому что угол меняется; статичные фигуры так рисовать дорого.
Где примитивы бьют спрайты#
| Задача | Инструмент | Почему примитив |
|---|---|---|
| Сетка уровня в редакторе | StrokeLine в цикле по шагу | спрайтам нужен тайл под каждую длину |
| Рамки и панели HUD | StrokeRect и FillRect | цвет меняется константой, атлас не растянешь |
| Индикаторы прогресса | Arc и StrokePath | угол считается кодом, а не режется из картинки |
| Прототип физики | FillCircle по точкам тел | коллизии видно сразу, до отрисовки спрайтов |
| Снаряды и пузыри | FillCircle | десяток заготовок заменяет одна функция |
| Финальный арт игры | спрайты и атласы | чистой геометрии не хватает выразительности |
Какой кусок кольца обведён при прогрессе перезарядки 0.25?
Прочитайте код и выберите, что он выведет:
// g.cooldown / g.cooldownMax == 0.25
start := float32(-math.Pi / 2)
end := start + 2*math.Pi*(1-g.cooldown/g.cooldownMax)
ring.Arc(600, 40, 16, start, end, vector.Clockwise)
vector.StrokePath(screen, &ring, ...)Цена: строить путь каждый кадр дорого#
Примитивы дёшевы только на первый взгляд: каждый вызов FillCircle внутри собирает путь из дуги на десятки сегментов, триангулирует его и отдаёт видеокарте. Десяток кругов на HUD — незаметно, тысяча частиц-кругов в каждом кадре — уже ощутимо, и тут начинается экономика. Фигуры, которые не меняются между кадрами, обязаны кэшироваться: vector.Path собирается один раз и хранится в поле структуры, а Draw рисует готовый путь вызовом FillPath или StrokePath — геометрия не пересчитывается, платится только вывод. Если фигура всё-таки поменялась, Reset() чистит путь, и он собирается заново — редкое событие, а не каждый тик.
Второй рычаг — сглаживание: флаг antialias и AntiAlias в опциях включают дополнительную работу на каждый пиксель края, и на сотнях объектов разница в кадрах видна без замеров. Правило: сглаживание — крупному UI и фигурам на переднем плане, без сглаживания — массовым объектам и отладочным слоям. Полную карту таких цен и способы замера соберёт урок про производительность: примитивы стоят там в одном ряду с DrawImage и текстом, и кэш путей — первый пункт их оптимизации. Практический ориентир такой: HUD и отладочные слои живут без кэша спокойно, а всё, что рисуется циклом по десяткам объектов, обязано либо кэшировать пути, либо переходить на готовые функции, либо уходить в предрендер — один раз нарисованная в offscreen-картинку сетка дешевле векторной в разы.
Что делает последний аргумент bool в вызове vector.StrokeLine?
Кэш пути работает, пока путь не мутирует. Типовая ошибка — держать один Path и в каждом кадре докидывать в него новые LineTo, не начав с Reset: путь накапливает сегменты, на первом кадре линия одна, на сотом — сто, и StrokePath честно обводит все сто, съедая кадр целиком. Симптом узнаваемый: игра тормозит тем сильнее, чем дольше работает, а на отдельном кадре в отладке всё чисто. Дисциплина: путь, собираемый каждый кадр, живёт локально в Draw и умирает; кэшированный — собирается один раз и после Reset, а не дописывается с конца.
Хитбоксы на примитивах: круг против прямоугольника#
Примитивы — не только картинка, но и геометрия, которую удобно проверять на пересечения. Круг с кругом проверяется расстоянием: сумма радиусов больше дистанции между центрами — столкнулись, а корень из квадрата расстояния извлекать не обязательно, достаточно сравнить квадраты. Круг с прямоугольником проверяется ближайшей точкой: координаты центра зажимаются в границы прямоугольника, и если зажатая точка ближе радиуса — пересечение есть. Углы обрабатываются сами собой: ближайшая точка может совпасть с углом прямоугольника, и формула это покрывает без отдельных веток.
Такая геометрия ложится прямо поверх AABB-коллизий: там мир проверялся прямоугольниками, здесь к ним добавляются круги — снаряды, зоны взрыва, радиусы подбора. Практика прототипа физики: тела рисуются FillCircle по тем же координатам, по которым проверяются пересечения, поэтому логика и картинка не разъезжаются, а любая ошибка в движении видна на первом же кадре. Круговой хитбокс ещё и удобнее для геймплея: он не имеет углов, игрок прощает ему промахи на волосок, и вращение спрайта не ломает форму проверки — повёрнутый прямоугольник потребовал бы отдельной математики, круг от поворота не меняется. По этой формуле стоит устроить быструю проверку широким кругом перед точной: сначала грубый фильтр по расстоянию центров, потом детальная проверка — это тот же принцип грубого и точного прохода, на котором стоят все коллизии в играх.
Правила примитивов одним списком#
- Готовые функции FillCircle, StrokeCircle, StrokeLine, FillRect, StrokeRect закрывают HUD и сетки
- DrawFilledCircle и другие старые имена устарели с версии 2.9: используйте актуальные
- Path собирается из MoveTo, LineTo, Arc и Close и отдаётся в FillPath или StrokePath
- Флаг antialias — крупному UI, массовые объекты рисуются без сглаживания
- Меняющаяся фигура строится заново каждый кадр, статичная кэшируется в поле структуры
- Мутирующий кэш-путь начинается с Reset, иначе сегменты копятся и кадр дорожает
- Хитбоксы: квадраты расстояний для кругов, зажим ближайшей точки для круга с прямоугольником
- Прототип физики рисуется теми же координатами, что проверяются на пересечение
Соберите путь из сегментов
Восстановите два метода: постановка пера и сегмент линии.
Рисование примитивов требует окна и проверяется в локальном Go, но геометрия урока — чистые вычисления: зажим координат, квадраты расстояний, углы дуги. Тематическая программа в песочнице сайта гоняет проверку круг-прямоугольник на десятках пар точек и печатает прогресс дуги по шагам — та же арифметика, что в circleHitsRect и кулдауне, только без холста.
Проверка круг-прямоугольник
Соберите функцию пересечения круга с прямоугольником.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Примитивы и итоговый проект#
В мини-аркаде, к которой курс идёт, примитивы займут место по силе: рамка HUD и полоса здоровья — StrokeRect и FillRect из этого урока, кулдаун способности — дуга Arc, отладочные коллизии при доводке — StrokeCircle вокруг каждого хитбокса, включаемые одной константой. Отладочный слой из примитивов — приём профессиональных студий: спрайты могут врать про границы тела, круги и прямоугольники вокруг тел — никогда, и расхождение физики с картинкой ловится мгновенно, до воспроизведения бага игроком. Тот же слой помогает настройке: радиус подбора, длина луча, зона взрыва — всё это числа в коде, но включённый слой превращает их в видимые окружности, и балансировка из гадания становится настройкой по линейке.
Каркас итогового проекта выигрывает и от того, что примитивы не добавляют ассетов: игре не нужны файлы, чтобы выглядеть собранной на ранних этапах. Стиль из чистой геометрии — честный минимум: рамка, круг, линия, текст из урока про шрифты. Этот минимум показывает механику в чистом виде, пока художественные ассеты дозревают в очереди на замену, и в прототипной фазе выглядит аккуратнее случайных заглушек.
Урок закрыл дыру между спрайтами и текстом: игра умеет рисовать геометрию — от рамки HUD до дуги перезарядки — не тратя ни одного файла ассетов. Пять готовых функций покрывают типовой UI, Path собирает нестандартные фигуры, кэш путей бережёт кадр, а хитбоксы на кругах и прямоугольниках делают прототип физики честным. Приёмы урока складываются в рабочий набор: статичный HUD без кэша, динамичные фигуры на готовых функциях, отладочный слой на одной константе. Дальше курс считает кадры: урок про производительность покажет, как замерять цену этих вызовов и где примитивам придётся уступить атласам. Домашнее задание соберёт HUD вашего проекта из примитивов — и он будет выглядеть готовым раньше, чем появятся настоящие спрайты.
Термины урока
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Пакет vector рисует UI без картинок: пять готовых функций для кругов, линий и прямоугольников.
- DrawFilledCircle устарела с версии 2.9 — актуальные имена начинаются с Fill и Stroke.
- Path собирает нестандартные фигуры из MoveTo, LineTo и Arc и отдаётся в FillPath или StrokePath.
- Сглаживание — последний аргумент готовых функций и AntiAlias в опциях: оно не бесплатное.
- Меняющиеся фигуры строятся заново, статичные кэшируются в поле структуры с Reset при мутации.
- Кулдаун-дуга — Arc по углу прогресса; трасса выстрела — StrokeLine по двум точкам.
- Хитбоксы примитивов: квадраты расстояний для кругов, зажим ближайшей точки для прямоугольников.
Домашнее задание
Соберите HUD проекта из примитивов: рамку экрана через StrokeRect, полосу здоровья через FillRect с шириной от hp, круг перезарядки способности через Arc и StrokePath с углом от прогресса. Добавьте отладочный слой на клавишу F3: StrokeCircle вокруг каждого хитбокса и StrokeLine между парами сталкивающихся тел — и проверьте, что круги сидят точно на физике. Реализуйте circleHitsRect для снарядов и зон подбора, сравните с прямоугольными проверками из урока про коллизии: расхождение на углах нормально, круг честнее. Заметите подтормаживание — кэшируйте статичные пути в полях структуры и сверьте TPS до и после. В конце включите и выключите antialias на массовом объекте: разница в кадрах должна быть видна на сотнях кругов.
Личные заметкиПусто
Вопросы и ответы по уроку
Почему координаты примитивов float32, а не float64?
Пакет целится в видеокарту, где float32 — родной формат вершин: конверсия на границе уже ничего не экономит. Для игровых координат тридцати двух бит хватает с запасом, а переход из float64 в float32 проще делать один раз, на границе пакета.
Чем FillCircle отличается от Path с дугой на два пи?
Результат один: готовая функция внутри строит ровно такой путь, но в оптимизированном виде с переиспользованием вершин. Практическое правило: стандартная фигура — готовая функция, нестандартная или составная — собственный Path.
Можно ли примитивами нарисовать весь прототип игры?
Да, и это рабочий приём: круги-игроки, прямоугольники-стены, линии-лучи проверяют механику до появления арта. Прототип на примитивах живёт днями, а не часами, потому что правка цвета и формы — константа в коде, а не перерисовка ассета.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Chromedp в Go: автоматизация Chrome без Selenium
Курс chromedp для Go: навигация, селекторы, клики и формы, скриншоты и PDF, JS-оценка, вкладки, прокси, пул браузеров и скрапинг. 25 уроков.
- Запускать Chrome из Go и управлять страницами
- Находить элементы селекторами и ждать их появления
- Кликать, заполнять формы и извлекать данные
- Делать скриншоты, PDF и выполнять JavaScript