Коротко
HUD рисуют в экранных координатах после мира, привязывают каждый элемент якорем к углу или краю и масштабируют от опорного разрешения с нижней границей. На телефонах к отступам добавляют безопасные зоны env(safe-area-inset-*), а зоны касания делают не меньше 44×44 CSS-пикселей. Ниже — формулы якорей и масштаба, таблица раскладки HUD и код, проверенный в Node.js 24 на экранах 1280×720 и 844×390.
Содержание
- Мировые координаты против экранных
- Якоря и отступы: формула позиции
- Масштаб интерфейса от опорного разрешения
- Безопасные зоны: вырез и полоса жестов
- Размер цели касания: 24 и 44 CSS-пикселя
- Таблица: раскладка HUD платформера
- Код на JavaScript: якоря, масштаб, зона нажатия
- Практический сценарий: проверка HUD на трёх экранах
- Когда вам это не подойдёт
Мировые координаты против экранных
HUD живёт в экранных координатах: его позиция отсчитывается от угла окна и не зависит от камеры. Мир рисуется со смещением камеры, поэтому счётчик очков, нарисованный «в мире», уезжает влево, когда герой бежит вправо. Порядок кадра такой: сначала мир со смещением камеры, затем сброс смещения, затем HUD.
Обратное преобразование нужно, когда касание по экрану должно попасть в объект мира:
мир_x = экран_x / зум + камера_x − ширина_экрана / (2 × зум)
Касание сначала проверяют по элементам HUD и только если оно мимо, переводят в мировые координаты.
Якоря и отступы: формула позиции
Якорь задаёт, к какой точке экрана привязан элемент, в долях: 0 — левый или верхний край, 0,5 — середина, 1 — правый или нижний. Позиция левого верхнего угла элемента:
x = левая_граница + (правая_граница − левая_граница) × якорь_x − ширина × якорь_x
левая_граница = безопасная_зона_слева + отступ
правая_граница = ширина_экрана − безопасная_зона_справа − отступ
Для вертикали формула та же. Одна формула покрывает все девять позиций: углы, середины краёв и центр.
Масштаб интерфейса от опорного разрешения
Масштаб HUD считают от опорного разрешения как меньшее из отношений ширины и высоты и ограничивают снизу и сверху:
масштаб = clamp(min(ширина / 1280, высота / 720), 0,75, 2)
Без нижней границы на телефоне в альбомной ориентации 844×390 масштаб был бы 0,54, и значок паузы 32×32 сжался бы до 17×17 — по нему трудно попасть. Без верхней на мониторе 2560×1440 счётчик стал бы огромным. Размеры шрифтов умножают на тот же масштаб.
Безопасные зоны: вырез и полоса жестов
На телефонах с вырезом и скруглёнными углами часть экрана перекрыта, и отступы HUD нужно увеличить на размер безопасной зоны. В CSS её дают переменные env(safe-area-inset-top), env(safe-area-inset-right), env(safe-area-inset-bottom) и env(safe-area-inset-left); у функции env() есть запасное значение вторым аргументом, например env(safe-area-inset-right, 0px). Игре на холсте эти значения удобно прочитать через вычисленные стили вспомогательного элемента и передать в расчёт якорей.
В альбомной ориентации вырез оказывается слева или справа, а полоса жестов — снизу, поэтому учитывайте все четыре стороны.
Размер цели касания: 24 и 44 CSS-пикселя
Минимальный размер цели касания задают критерии WCAG 2.2, и считается он в CSS-пикселях, а не в физических. Критерий 2.5.8 (уровень AA) требует не меньше 24×24 CSS-пикселя либо достаточного отступа: круг диаметром 24 CSS-пикселя по центру маленькой цели не должен задевать соседние. Критерий 2.5.5 (уровень AAA) — не меньше 44×44 CSS-пикселя.
Для игры ориентируйтесь на 44×44 и больше: по зоне прыжка бьют не глядя и в спешке. Видимый значок может быть меньше, если зона нажатия расширена вокруг него, — так делает функция hitBox в коде ниже.
Таблица: раскладка HUD платформера
Значения ниже заданы для опорного разрешения 1280×720 и умножаются на масштаб.
| Элемент | Якорь | Размер | Отступ | Зона нажатия |
|---|---|---|---|---|
| Очки | верхний левый | 160 × 32 | 16 | — |
| Здоровье | верхний левый, под очками | 160 × 16 | 16 | — |
| Пауза | верхний правый | 32 × 32 | 16 | не меньше 44 × 44 |
| Прыжок | нижний правый | 72 × 72 | 24 | сам элемент |
| Стик движения | нижний левый | 120 × 120 | 24 | вся левая треть экрана |
Код на JavaScript: якоря, масштаб, зона нажатия
Код считает позиции элементов HUD для монитора и телефона с вырезом и расширяет зону нажатия маленького значка. Он проверен в Node.js 24, числа в комментариях совпадают с выводом.
// Якорь элемента HUD: доля ширины и высоты экрана (0 — левый/верхний край, 1 — правый/нижний)
const ANCHORS = {
topLeft: [0, 0], top: [0.5, 0], topRight: [1, 0],
bottomLeft: [0, 1], bottom: [0.5, 1], bottomRight: [1, 1],
};
// Масштаб интерфейса от опорного разрешения 1280×720 с нижней и верхней границей
function uiScale(view) {
const s = Math.min(view.w / 1280, view.h / 720);
return Math.max(0.75, Math.min(2, s));
}
// Позиция левого верхнего угла элемента в CSS-пикселях с учётом безопасной зоны (вырез, скругления)
function place(el, view, safe) {
const k = uiScale(view);
const a = ANCHORS[el.anchor];
const w = el.w * k, h = el.h * k;
const left = safe.left + el.margin * k;
const right = view.w - safe.right - el.margin * k;
const top = safe.top + el.margin * k;
const bottom = view.h - safe.bottom - el.margin * k;
const x = left + (right - left) * a[0] - w * a[0];
const y = top + (bottom - top) * a[1] - h * a[1];
return { x: Math.round(x), y: Math.round(y), w: Math.round(w), h: Math.round(h) };
}
// Кнопка касания: видимый значок может быть меньше, но зона нажатия — не меньше minSide
function hitBox(r, minSide) {
const w = Math.max(r.w, minSide), h = Math.max(r.h, minSide);
return { x: r.x - (w - r.w) / 2, y: r.y - (h - r.h) / 2, w, h };
}
const pause = { anchor: "topRight", w: 32, h: 32, margin: 16 };
const score = { anchor: "topLeft", w: 160, h: 32, margin: 16 };
const jump = { anchor: "bottomRight", w: 72, h: 72, margin: 24 };
const none = { top: 0, right: 0, bottom: 0, left: 0 };
const desktop = { w: 1280, h: 720 };
console.log(uiScale(desktop), place(pause, desktop, none)); // 1 { x: 1232, y: 16, w: 32, h: 32 }
// Телефон в альбомной ориентации 844×390 с вырезом слева 47 и полосой жестов снизу 21
const phone = { w: 844, h: 390 };
const notch = { top: 0, right: 47, bottom: 21, left: 47 };
console.log(uiScale(phone)); // 0.75 — нижняя граница, иначе было бы 0.54
console.log(place(score, phone, notch)); // { x: 59, y: 12, w: 120, h: 24 }
console.log(place(jump, phone, notch)); // { x: 725, y: 297, w: 54, h: 54 }
// Значок паузы 24×24 на телефоне: зона нажатия расширяется до 44×44
const p = place(pause, phone, notch);
console.log(p.w, hitBox(p, 44)); // 24 { x: 751, y: 2, w: 44, h: 44 }
На телефоне значок паузы виден размером 24×24, но принимает касания в квадрате 44×44; часть этой зоны заходит в отступ, а не под вырез, потому что безопасная зона уже учтена в позиции.
Практический сценарий: проверка HUD на трёх экранах
Проверка занимает около 10 минут в инструментах разработчика браузера:
- Монитор 1280×720. Масштаб 1: элементы на своих местах, отступы 16 и 24 пикселя.
- Телефон в портретной ориентации 390×844. Масштаб упирается в нижнюю границу 0,75; проверьте, что очки и пауза не перекрываются на ширине 390.
- Телефон в альбомной ориентации 844×390. Задайте безопасные зоны слева и справа по 47, снизу 21 и убедитесь, что ни один элемент не заходит под них.
- Цели касания. Включите отображение зон нажатия и проверьте, что каждая не меньше 44×44 CSS-пикселей.
- Смена ориентации. Поверните устройство во время игры: позиции должны пересчитываться по событию изменения размера, без перезагрузки.
Когда вам это не подойдёт
Якоря с общим масштабом подходят большинству игр с HUD, но не всем:
- Сложные меню с прокруткой и формами: списки сохранений, магазин с фильтрами и поля ввода проще сверстать обычным HTML и CSS поверх холста, чем рассчитывать вручную.
- Диегетический интерфейс: если здоровье показано на самом персонаже, а патроны — на оружии, такие элементы живут в мировых координатах и двигаются с камерой.
- Игры только для мыши на компьютере: требования к целям касания там мягче, но минимум 24×24 CSS-пикселя по WCAG 2.5.8 всё равно полезен.
Мы разрабатываем J2DRU — браузерный 2D игровой движок; текст, кнопки и другие элементы интерфейса поверх игры входят в проверенные внутри разработки основы. Движок публично пока не открыт, заявку можно оставить на странице раннего доступа (подача заявки не гарантирует приглашение).
Источники (проверено: ноябрь 2026): W3C — Understanding SC 2.5.8 Target Size (Minimum), W3C — Understanding SC 2.5.5 Target Size (Enhanced), MDN — env().
Читайте также: