> For the complete documentation index, see [llms.txt](https://docs.nursultan.fun/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nursultan.fun/scripting-api-ru/interfeis/render-2d.md).

# Рендер 2D

Событие `Render2DEvent` вызывается каждый кадр на слое HUD. Оно несёт `render()`, размер экрана `width()` × `height()` и `tickDelta()`.

Вся отрисовка идёт через `e.render()`. Координаты — в UI-пикселях, тех же, что `width()` и `height()`. Цвета — обычные `Int` вида `0xAARRGGBB`, собрать их удобно через `Colors`.

```kotlin
on<Render2DEvent> { e ->
    val r = e.render()
    r.roundedRect(8f, 8f, 160f, 40f, 8f, Colors.rgba(0, 0, 0, 160))
    r.text("Nursultan", 16f, 16f, 14f, Colors.WHITE)
    r.text("fps " + client.fps(), 16f, 32f, 10f, Colors.CYAN)
}
```

## Фигуры

| Метод                                                | Что рисует              |
| ---------------------------------------------------- | ----------------------- |
| `rect(x, y, w, h, argb)`                             | залитый прямоугольник   |
| `roundedRect(x, y, w, h, radius, argb)`              | со скруглёнными углами  |
| `gradient(x, y, w, h, argbFrom, argbTo, horizontal)` | градиент из двух цветов |
| `outline(x, y, w, h, thickness, argb)`               | только рамка            |
| `circle(cx, cy, radius, argb)`                       | круг                    |
| `ring(cx, cy, radius, thickness, argb)`              | кольцо                  |
| `triangle(x1, y1, x2, y2, x3, y3, argb)`             | треугольник             |

## Размытие

Размывает то, что уже нарисовано под указанной областью — так делают подложки под панели:

```kotlin
r.blur(8f, 8f, 160f, 40f, 12f)
r.blur(8f, 8f, 160f, 40f, 12f, Colors.rgba(0, 0, 0, 80), 8f)
```

Второй вариант сразу заливает область цветом и скругляет углы.

## Текст

```kotlin
r.text("привет", 10f, 10f, 12f, Colors.WHITE)
r.textShadow("привет", 10f, 10f, 12f, Colors.WHITE, Colors.rgba(0, 0, 0, 200))

r.textWidth("привет", 12f)
r.textHeight(12f)
```

Ширину считают, чтобы подогнать подложку под текст:

```kotlin
val text = "здоровье " + player.health()
val width = r.textWidth(text, 10f) + 12f
r.roundedRect(6f, 6f, width, 18f, 5f, Colors.rgba(0, 0, 0, 140))
r.text(text, 12f, 11f, 10f, Colors.WHITE)
```

## Шрифты

Без указания шрифта используется шрифт клиента. Можно попросить ванильный или зарегистрировать свой:

```kotlin
r.text("привет", 10f, 10f, 12f, Colors.WHITE, "minecraft")

// один раз на верхнем уровне скрипта
font("myfont", "my-font.ttf")

// дальше по имени
r.text("привет", 10f, 30f, 12f, Colors.WHITE, "myfont")
```

TTF-файл кладётся в папку скриптов рядом с самим скриптом.

## Картинки и иконки

```kotlin
r.item(inventory.held(), 10f, 10f, 16f)          // предмет
r.item("minecraft:diamond", 30f, 10f, 16f)       // по идентификатору
r.head(playerEntity, 50f, 10f, 16f)              // голова игрока
r.effectIcon("speed", 70f, 10f, 16f)             // иконка эффекта
r.texture("minecraft:textures/item/apple.png", 90f, 10f, 16f, 16f)
r.image("logo.png", 110f, 10f, 32f, 32f)         // файл из папки скриптов
```

## Точка мира на экране

`project` переводит координаты мира в координаты экрана — так делают подписи над игроками:

```kotlin
on<Render2DEvent> { e ->
    val r = e.render()
    for (target in world.players()) {
        if (target.isSelf()) continue
        val point = r.project(target.position().add(0.0, target.height() + 0.4, 0.0))
        if (!point.visible()) continue
        val name = target.name()
        r.text(name, point.x() - r.textWidth(name, 8f) / 2f, point.y(), 8f, Colors.WHITE)
    }
}
```

`visible()` — `false`, если точка за спиной или за краем экрана.

## Плавность

Кадров больше, чем тиков, поэтому позиции, взятые из `position()`, будут дёргаться. Для рисования бери `renderPosition()` — она уже сглажена. Если считаешь что-то своё, используй `tickDelta()` из события.

## Не делай тяжёлое в кадре

Обработчик вызывается 60+ раз в секунду. Перебирать все сущности мира, считать лучи и запрашивать предсказание внутри него не надо — посчитай в `ClientTickEvent`, положи в переменную, а в кадре только рисуй.
