Hudifine
Создавай собственные HUD-оверлеи с помощью простого скриптового языка. Знаний в кодинге модов не требуется.
- Загрузчики
- Fabric
- Скачиваний на Modrinth
- 797
- Подписчиков на Modrinth
- 4
- версий
- 5
- Обновлён
- 19 июня 202619 июн.
- Опубликован
- в 2026
Коротко о моде
Ставится только в игру; на сервер ставить не нужно
Hudifine — это HUD-мод, который позволяет любому легко создавать HUD-ы с помощью HUD Script, отображающего выбранные вами метрики.
Откройте чат, чтобы добавить и настроить их.
Ниже приведена документация по HUDScript — скриптовому языку Hudifine.
Документация HUDScript
Документация для разработчиков HUD Script
HUD Script — это безопасный, изолированный формат скриптов для создания полностью настраиваемых игровых HUD-ов в Minecraft (Fabric). Скрипты вставляются и запускаются; среда разработки модов не требуется.
Содержание
- Обзор
- Структура скрипта
- Бюджет экрана (правило 30%)
Источники данных
- Игрок
- Мир
- Производительность
- Бой
- Инвентарь
- Ввод
- Окружение
- Состояние игры
- Мультиплеер
- Время
Система компоновки
- Порядок и слои
- Стилизация
- Элементы
- Условия и логика
- События
- Переключатели доступности
- API настроек игрока
- Анимации
- Руководство по реализации CPS
- Полные примеры
Обзор
HUD Script — это декларативный + событийно-ориентированный формат скриптов. Скрипты определяют:
- Какие данные отображать (из фиксированного набора разрешённых источников данных)
- Как их отображать (полная визуальная свобода в рамках бюджета экрана)
- Настройки, задаваемые игроком (доступность, переключатели, цвета и т. д.)
Скрипты изолированы по своей природе. Они могут только:
- ✅ Читать из разрешённых источников данных
- ✅ Отрисовывать в HUD-оверлей
- ✅ Визуально реагировать на ввод игрока
- ❌ Делать сетевые запросы
- ❌ Обращаться к файловой системе
- ❌ Взаимодействовать с чем-либо за пределами виджета
Структура скрипта
Каждый скрипт имеет три необязательных блока верхнего уровня:
meta {
name: "My Widget"
author: "yourname"
version: "1.0.0"
description: "A short description of what this widget does."
}
settings {
// Player-configurable options exposed in the right-click menu
}
widget {
// Layout, elements, styling, logic
}Обязателен только блок widget.
Бюджет экрана (правило 30%)
Виджет может занимать не более 30% площади экрана игрока в любой момент времени.
- Площадь экрана вычисляется как
screenWidth × screenHeight - Ограничивающий прямоугольник вашего виджета (все элементы вместе) не должен превышать
screenWidth × screenHeight × 0.30 - Это проверяется во время выполнения; если ваш виджет превышает 30%, он будет автоматически уменьшен, а в редакторе виджетов появится предупреждение
- Виджеты, которые отключены или полностью прозрачны, не учитываются в бюджете
- Несколько виджетов имеют каждый свой независимый бюджет в 30%
Совет: Разумно используйте свойство
sizeи тестируйте на разных разрешениях. Проектируйте исходя из 1920×1080.
Источники данных
Все источники данных доступны только для чтения. Вызывайте их внутри значений элементов с помощью get().
Игрок
| Источник | Возвращает | Описание |
|---|---|---|
player.health | float | Текущее здоровье (0.0-20.0) |
player.maxHealth | float | Максимальное здоровье, включая поглощение |
player.absorption | float | Сердца поглощения |
player.hunger | int | Уровень еды (0-20) |
player.saturation | float | Уровень насыщения (0.0-20.0) |
player.exhaustion | float | Значение истощения (0.0-4.0) |
player.air | int | Тики запаса воздуха (0-300) |
player.xp | int | Всего очков опыта |
player.xpLevel | int | Текущий уровень опыта |
player.xpProgress | float | Прогресс опыта до следующего уровня (0.0-1.0) |
player.speed | float | Текущая скорость передвижения (блоков/сек) |
player.isSprinting | bool | Бежит ли игрок |
player.isSneaking | bool | Крадётся ли игрок |
player.isSwimming | bool | Плывёт ли игрок |
player.isFlying | bool | Летит ли игрок (креатив/элитра) |
player.isFalling | bool | Падает ли игрок |
player.isOnGround | bool | Стоит ли игрок на земле |
player.isInWater | bool | Находится ли игрок в воде |
player.isInLava | bool | Находится ли игрок в лаве |
player.fallDistance | float | Текущая высота падения в блоках |
player.reachDistance | float | Текущая дальность досягаемости |
player.name | string | Имя игрока |
player.uuid | string | UUID игрока |
player.gamemode | string | survival, creative, adventure, spectator |
player.score | int | Текущий счёт игрока в таблице очков |
player.ping | int | Задержка в мс (только в мультиплеере) |
World
| Источник | Возвращает | Описание |
|---|---|---|
world.x | float | Координата X игрока |
world.y | float | Координата Y игрока |
world.z | float | Координата Z игрока |
world.blockX | int | Координата X блока игрока (округлённая вниз) |
world.blockY | int | Координата Y блока игрока (округлённая вниз) |
world.blockZ | int | Координата Z блока игрока (округлённая вниз) |
world.chunkX | int | Координата X текущего чанка |
world.chunkZ | int | Координата Z текущего чанка |
world.facing | string | Сторона света: N, NE, E, SE, S, SW, W, NW |
world.facingDegrees | float | Поворот в градусах (0-360) |
world.pitch | float | Наклон камеры (-90 до 90) |
world.yaw | float | Поворот камеры (0-360) |
world.dimension | string | overworld, nether, end или пользовательское пространство имён |
world.biome | string | Название текущего биома (например, minecraft:plains) |
world.lightLevel | int | Уровень освещения от блоков у ног игрока (0-15) |
world.skyLightLevel | int | Уровень небесного освещения в позиции игрока (0-15) |
world.moonPhase | int | Фаза луны (0-7) |
world.isRaining | bool | Идёт ли дождь |
world.isThundering | bool | Идёт ли гроза |
world.rainStrength | float | Интенсивность дождя (0.0-1.0) |
world.difficulty | string | peaceful, easy, normal, hard |
world.name | string | Название мира/сервера |
world.seed | string | Сид мира (только в одиночной игре, на серверах возвращает "hidden") |
world.spawnX | int | Координата X точки появления мира |
world.spawnZ | int | Координата Z точки появления мира |
world.distanceToSpawn | float | Расстояние до точки появления мира в блоках |
Performance
| Источник | Возвращает | Описание |
|---|---|---|
perf.fps | int | Текущее количество кадров в секунду |
perf.fpsAvg | float | Средний FPS за последние 5 секунд |
perf.fpsMin | int | Минимальный FPS за последние 5 секунд |
perf.fpsMax | int | Максимальный FPS за последние 5 секунд |
perf.frameTime | float | Время последнего кадра в миллисекундах |
perf.frameTimeAvg | float | Среднее время кадра за последние 5 секунд |
perf.tps | float | Тиков в секунду на сервере (только в мультиплеере) |
perf.mspt | float | Миллисекунд на тик (сервер, только в мультиплеере) |
perf.chunkUpdates | int | Обновлений чанков за этот кадр |
perf.renderedChunks | int | Количество отрисованных чанков |
perf.entities | int | Сущностей в дальности прорисовки |
perf.blockEntities | int | Блочных сущностей в дальности прорисовки |
perf.particles | int | Активных частиц |
Combat
| Источник | Возвращает | Описание |
|---|---|---|
combat.attackCooldown | float | Прогресс перезарядки атаки (0.0-1.0) |
combat.attackCooldownMs | int | Миллисекунд до полной перезарядки атаки |
combat.isAttacking | bool | Атакует ли игрок прямо сейчас |
combat.lastDamage | float | Последний полученный урон |
combat.lastDamageSource | string | Источник последнего урона (например, fire, fall, player) |
combat.killCount | int | Убийств игроком за эту сессию |
combat.deathCount | int | Смертей игрока за эту сессию |
combat.kdr | float | Соотношение убийств/смертей за эту сессию |
combat.targetName | string | Имя сущности, на которую смотрит игрок (если есть) |
combat.targetHealth | float | Здоровье сущности, на которую смотрит игрок |
combat.targetMaxHealth | float | Максимальное здоровье сущности, на которую смотрит игрок |
combat.targetDistance | float | Расстояние до целевой сущности |
combat.targetType | string | Тип сущности, на которую смотрит игрок |
combat.isInCombat | bool | Получал ли игрок урон или наносил ли его недавно |
combat.combatTimer | int | Секунд с последнего боевого действия |
Инвентарь
| Источник | Возвращает | Описание |
|---|---|---|
inventory.hotbarSlot | int | Текущий слот хотбара (0-8) |
inventory.mainHandItem | string | ID предмета в основной руке |
inventory.mainHandCount | int | Количество предметов в стаке в основной руке |
inventory.mainHandDurability | int | Прочность предмета в основной руке |
inventory.mainHandMaxDurability | int | Максимальная прочность предмета в основной руке |
inventory.mainHandDurabilityPct | float | Прочность предмета в основной руке в % (0.0-1.0) |
inventory.offHandItem | string | ID предмета во второй руке |
inventory.offHandCount | int | Количество предметов в стаке во второй руке |
inventory.offHandDurability | int | Прочность предмета во второй руке |
inventory.helmetItem | string | ID шлема |
inventory.helmetDurability | int | Прочность шлема |
inventory.helmetDurabilityPct | float | Прочность шлема в % (0.0-1.0) |
inventory.chestplateItem | string | ID нагрудника |
inventory.chestplateDurability | int | Прочность нагрудника |
inventory.chestplateDurabilityPct | float | Прочность нагрудника в % (0.0-1.0) |
inventory.leggingsItem | string | ID поножей |
inventory.legginsDurability | int | Прочность поножей |
inventory.leggingsDurabilityPct | float | Прочность поножей в % (0.0-1.0) |
inventory.bootsItem | string | ID ботинок |
inventory.bootsDurability | int | Прочность ботинок |
inventory.bootsDurabilityPct | float | Прочность ботинок в % (0.0-1.0) |
inventory.arrowCount | int | Количество стрел в инвентаре |
inventory.totalSlots | int | Всего слотов в инвентаре (36) |
inventory.usedSlots | int | Количество занятых слотов в инвентаре |
inventory.emptySlots | int | Количество пустых слотов в инвентаре |
inventory.xpBottleCount | int | Пузырьки опыта в инвентаре |
Ввод
Источники ввода отражают то, что игрок физически нажимает. Они только для отображения; вы не можете перехватывать, блокировать или перенаправлять ввод.
| Источник | Возвращает | Описание |
|---|---|---|
input.forward | bool | Клавиша W зажата |
input.backward | bool | Клавиша S зажата |
input.left | bool | Клавиша A зажата |
input.right | bool | Клавиша D зажата |
input.jump | bool | Пробел зажат |
input.sneak | bool | Shift зажат |
input.sprint | bool | Клавиша спринта зажата |
input.attack | bool | Левая кнопка мыши зажата |
input.use | bool | Правая кнопка мыши зажата |
input.drop | bool | Клавиша Q зажата |
input.inventory | bool | Клавиша инвентаря зажата |
input.swap | bool | Клавиша F (смена рук) зажата |
input.hotbar1-input.hotbar9 | bool | Клавиши слотов хотбара зажаты |
input.cps | int | CPS левой кнопки (кликов в секунду) |
input.rcps | int | CPS правой кнопки |
input.mouseX | int | Позиция мыши по X на экране |
input.mouseY | int | Позиция мыши по Y на экране |
input.mouseDeltaX | float | Смещение мыши по X за этот кадр |
input.mouseDeltaY | float | Смещение мыши по Y за этот кадр |
input.sensitivity | float | Текущая чувствительность мыши (0.0-1.0) |
Окружение
| Источник | Возвращает | Описание |
|---|---|---|
env.timeOfDay | int | Тики мира (0-24000) |
env.timeString | string | Форматированное время (например, 6:00 AM) |
env.dayCount | int | Прошло игровых дней |
env.temperature | float | Температура биома на позиции игрока |
env.humidity | float | Влажность биома на позиции игрока |
env.canSeeSky | bool | Видит ли игрок небо |
env.isUnderground | bool | Находится ли игрок под землёй (нет доступа к небу) |
env.nearestVillageDistance | float | Расстояние до ближайшей обнаруженной деревни (в блоках) |
env.isInVillage | bool | Находится ли игрок внутри деревни |
env.nearestPlayerDistance | float | Расстояние до ближайшего другого игрока (мультиплеер) |
env.nearestPlayerName | string | Имя ближайшего другого игрока |
Состояние игры
| Источник | Возвращает | Описание |
|---|---|---|
game.isPaused | bool | Приостановлена ли игра |
game.isInGui | bool | Открыт ли какой-либо GUI |
game.isInventoryOpen | bool | Открыт ли инвентарь |
game.isInBed | bool | Спит ли игрок |
game.isRiding | bool | Едет ли игрок на сущности |
game.ridingEntityType | string | Тип сущности, на которой едет игрок |
game.ridingEntityHealth | float | Здоровье сущности, на которой едет игрок |
game.isElytraFlying | bool | Летит ли игрок на элитрах |
game.elytraHealth | int | Текущая прочность элитр |
game.elytraSpeed | float | Текущая скорость полёта на элитрах (блоков/сек) |
game.potionEffects | `list` | ID активных эффектов зелий |
game.potionEffectAmplifiers | map<string, int> | Уровень каждого активного эффекта |
game.potionEffectDurations | map<string, int> | Длительность в тиках каждого активного эффекта |
game.scoreboardObjective | string | Название текущей цели таблицы очков в сайдбаре |
game.scoreboardScore | int | Очки игрока по текущей цели в сайдбаре |
game.bossBarName | string | Название активного боссбара (если есть) |
game.bossBarProgress | float | Прогресс боссбара (0.0-1.0) |
game.isSpectatingEntity | bool | Наблюдает ли игрок за сущностью |
game.spectatingEntityType | string | Тип сущности, за которой наблюдает игрок |
Мультиплеер
| Источник | Возвращает | Описание |
|---|---|---|
server.name | string | Название сервера / IP |
server.motd | string | MOTD сервера |
server.playerCount | int | Текущее количество игроков онлайн |
server.maxPlayers | int | Максимальное количество игроков на сервере |
server.ping | int | Пинг до сервера в мс |
server.tps | float | TPS сервера |
server.isLAN | bool | Подключён ли игрок к LAN-серверу |
server.isSingleplayer | bool | Играет ли игрок в одиночной игре |
server.version | string | Версия Minecraft на сервере |
Время
| Источник | Возвращает | Описание |
|---|---|---|
clock.hour | int | Реальный час (0-23) |
clock.minute | int | Реальная минута (0-59) |
clock.second | int | Реальная секунда (0-59) |
clock.timeString | string | Форматированное реальное время (например, 14:32) |
clock.timeString12 | string | 12-часовой формат (например, 2:32 PM) |
clock.sessionTime | int | Секунды в текущей игровой сессии |
clock.sessionTimeString | string | Форматированное время сессии (например, 1h 23m) |
clock.date | string | Реальная дата (например, 2025-04-26) |
clock.unixTimestamp | int | Unix-время (секунды) |
Система компоновки
Виджеты используют простую блочную компоновку с привязкой к позициям на экране.
Точки привязки
widget {
anchor: top-left // top-left, top-center, top-right
// middle-left, center, middle-right
// bottom-left, bottom-center, bottom-right
offsetX: 10 // Смещение в пикселях от точки привязки по X (может быть отрицательным)
offsetY: 10 // Смещение в пикселях от точки привязки по Y (может быть отрицательным)
}Размеры
widget {
width: 120 // Фиксированная ширина в пикселях
height: auto // "auto" подстраивается под содержимое, либо фиксированные пиксели
padding: 8 // Одинаковый отступ (или padding: 8 4 для вертикального/горизонтального)
margin: 4 // Внешний отступ
}Направление
widget {
direction: column // "column" (вертикальный стек) или "row" (горизонтальный стек)
gap: 4 // Промежуток между дочерними элементами в пикселях
align: start // "start", "center", "end" - выравнивание по поперечной оси
justify: start // "start", "center", "end", "space-between" - выравнивание по главной оси
}
### Z-Index
```hud
widget {
zIndex: 10 // Чем выше значение, тем поверх остальных виджетов рендерится
}Порядок и слои
По умолчанию элементы рендерятся в том порядке, в котором они написаны в скрипте, сверху вниз. Это можно переопределить свойством order у любого элемента или группы.
widget {
direction: column
text { value: "I appear second" order: 2 color: #ffffff fontSize: 12 }
text { value: "I appear first" order: 1 color: #aaaaaa fontSize: 10 }
text { value: "I appear third" order: 3 color: #555555 fontSize: 9 }
}order работает как CSS order: меньшие числа рендерятся первыми (выше или левее — зависит от direction). Элементы без order по умолчанию имеют order: 0 и рендерятся раньше явно упорядоченных.
order также можно использовать внутри group:
group {
direction: row
text { value: "CPS" order: 2 color: #aaaaaa fontSize: 9 }
keyIndicator { key: input.attack label: "LMB" order: 1 size: 20 borderRadius: 8 activeColor: #ffffff inactiveColor: #ffffff33 }
}Совет: используй
order, когда хочешь сгруппировать логику скрипта, но при этом изменить визуальное расположение. Если у всего есть явный order, чище просто переписать скрипт в нужном порядке.
Стилизация
Фон
widget {
background: #00000088 // Hex-цвет с необязательной альфой (последние 2 цифры)
background: transparent // Без фона
borderRadius: 6 // Скругление углов в пикселях
border: 1 #ffffff44 // Рамка: ширина цвет
backdropBlur: 4 // Размытие пикселей позади виджета (зависит от GPU)
}Стилизация текста
text {
value: get(player.health)
color: #ffffff
fontSize: 12 // В пикселях (рекомендуется 8-32)
fontWeight: bold // "normal", "bold"
fontStyle: normal // "normal", "italic"
shadow: true // Тень текста в стиле Minecraft
shadowColor: #00000066
letterSpacing: 0 // Пиксели между символами
lineHeight: 1.2 // Множитель высоты строки
align: left // "left", "center", "right"
truncate: 20 // Максимум символов до обрезки (опционально)
uppercase: false // Принудительный верхний регистр
}Цвета
Цвета — это hex-строки: #RRGGBB или #RRGGBBAA (с альфой).
color: #ff0000 // Красный, полностью непрозрачный
color: #ff000088 // Красный, 50% прозрачности
color: #fff // Сокращённая запись (разворачивается в #ffffff)Прозрачность
widget {
opacity: 0.8 // От 0.0 (невидимый) до 1.0 (полностью непрозрачный)
}Элементы
Элементы — это визуальные строительные блоки внутри блока widget.
text
Отображает строковое значение.
text {
value: "FPS: {get(perf.fps)}" // Встроенные выражения с {}
color: #ffffff
fontSize: 12
shadow: true
}bar
Полоса заполнения (здоровье, опыт, перезарядка и т. д.)
bar {
value: get(player.health)
max: get(player.maxHealth)
width: 100
height: 8
fillColor: #ff4444
backgroundColor: #00000066
borderRadius: 4
direction: left-to-right // "left-to-right", "right-to-left", "bottom-to-top", "top-to-bottom"
}icon
Отрисовывает иконку предмета или блока Minecraft.
icon {
item: get(inventory.mainHandItem) // Строка ID предмета
size: 16 // Размер иконки в пикселях
}circle
Круговой индикатор прогресса.
circle {
value: get(combat.attackCooldown) // 0.0-1.0
radius: 12
strokeWidth: 3
color: #ffffff
backgroundColor: #ffffff33
startAngle: -90 // Градусы — 0 = справа, -90 = сверху
direction: clockwise // "clockwise" или "counterclockwise"
}
### `image`
Отображает статичную текстуру из ресурспака.
```hud
image {
texture: "hud:textures/my_icon.png" // Namespace:path within resource pack
width: 16
height: 16
tint: #ffffff // Optional color tint
}keyIndicator
Показывает визуальную клавишу, которая подсвечивается при нажатии.
keyIndicator {
key: input.forward
label: "W"
activeColor: #ffffff
inactiveColor: #ffffff44
size: 20 // Width and height of the key in pixels
width: 20 // Override width only (useful for SPACE, LMB, RMB)
borderRadius: 8 // Higher = more rounded. Use 999 for a full pill shape
fontSize: 9 // Label font size inside the key
shadow: true // Text shadow on label
}Совет: Для широких клавиш вроде SPACE, LMB и RMB ставь
widthбольшеsize, чтобы растянуть их по горизонтали, сохранив ту же высоту.borderRadiusот8до12соответствует виду Lunar Client.
group
Контейнер для группировки и вложения элементов друг в друга.
group {
direction: row
gap: 4
background: #00000066
padding: 6
borderRadius: 4
text { value: "Health: " color: #aaaaaa fontSize: 11 }
text { value: "{get(player.health)}" color: #ff4444 fontSize: 11 }
}separator
Горизонтальная или вертикальная разделительная линия.
separator {
direction: horizontal // "horizontal" or "vertical"
color: #ffffff22
thickness: 1
length: 80 // Pixels, or "auto" to fill available space
margin: 4
}spacer
Пустое пространство между элементами.
spacer {
size: 8
}Условия и логика
if / else
Показывают или скрывают элементы в зависимости от условий.
if get(player.health) < 6 {
text {
value: "⚠ LOW HEALTH"
color: #ff0000
fontSize: 14
shadow: true
}
} else if get(player.health) < 10 {
text {
value: "Health is getting low"
color: #ffaa00
fontSize: 12
}
} else {
// nothing
}Встроенные выражения
Используй {} внутри строковых значений для встроенных выражений:
text { value: "You are in the {get(world.dimension)}" }
text { value: "XP Level: {get(player.xpLevel)} ({round(get(player.xpProgress) * 100)}%)" }Математические функции
| Функция | Описание |
|---|---|
round(x) | Округление до ближайшего целого |
floor(x) | Округление вниз |
ceil(x) | Округление вверх |
abs(x) | Модуль числа |
min(x, y) | Минимум из двух значений |
max(x, y) | Максимум из двух значений |
clamp(x, min, max) | Ограничение значения между min и max |
lerp(a, b, t) | Линейная интерполяция |
pct(value, max) | Возвращает (value / max) * 100 |
format(x, decimals) | Форматирует число до N знаков после запятой |
Строковые функции
| Функция | Описание |
|---|---|
upper(s) | Верхний регистр строки |
lower(s) | Нижний регистр строки |
concat(a, b) | Объединяет две строки |
trim(s, n) | Обрезает строку до N символов |
replace(s, from, to) | Заменяет подстроку |
Операторы сравнения
==, !=, <, >, <=, >=, &&, ||, !
if get(world.dimension) == "nether" && get(player.health) < 10 {
text { value: "Danger!" color: #ff0000 }
}События
Реагируй на события, чтобы визуально обновлять виджет.
on keypress(input.attack) {
// Triggered once when key is first pressed
text {
value: "Attacking!"
color: #ff4444
fadeOut: 500 // Fade out after 500ms
}
}
on keyhold(input.sneak) {
// Triggered every tick while key is held
}
on keyrelease(input.jump) {
// Triggered once when key is released
}
on event(player.health < 5) {
// Triggered when condition becomes true
// Re-triggers if condition goes false then true again
}
on tick {
// Runs every game tick (20 times/sec)
}
on frame {
// Runs every render frame
}
---
## Переключатели доступности
Разработчики могут добавлять переключатели и настройки для игроков с особыми потребностями доступности. Они отображаются в панели настроек по правому клику на виджете.
```hud
settings {
toggle "High Contrast Mode" {
id: highContrast
default: false
description: "Increases color contrast for better visibility."
}
toggle "Reduce Motion" {
id: reduceMotion
default: false
description: "Disables animations and transitions."
}
toggle "Large Text" {
id: largeText
default: false
description: "Increases all text sizes by 1.5×."
}
toggle "Colorblind Mode" {
id: colorblindMode
default: false
description: "Replaces color-coded indicators with shapes/patterns."
}
toggle "Screen Reader Hints" {
id: screenReaderHints
default: false
description: "Shows additional text labels on icon-only elements."
}
}Затем используйте значения настроек в своём виджете:
widget {
text {
value: "HP: {get(player.health)}"
color: if setting(highContrast) then #ffff00 else #ffffff
fontSize: if setting(largeText) then 18 else 12
shadow: true
}
}API настроек игрока
Помимо переключателей доступности, вы можете добавлять полностью настраиваемые игроком параметры.
Типы настроек
settings {
// Выбор цвета
color "Health Bar Color" {
id: healthColor
default: #ff4444
description: "Color of the health bar fill."
}
// Слайдер (числовой диапазон)
slider "Widget Opacity" {
id: widgetOpacity
min: 0.1
max: 1.0
step: 0.05
default: 0.85
description: "Transparency of the widget background."
}
// Выпадающий список
select "Display Mode" {
id: displayMode
options: ["compact", "normal", "expanded"]
default: "normal"
description: "How much information to show."
}
// Текстовое поле (только отображение метки)
text "Custom Label" {
id: customLabel
default: "My Widget"
maxLength: 20
description: "Custom title displayed at the top."
}
// Переключатель (булево значение)
toggle "Show Widget" {
id: showWidget
default: true
description: "Toggle the entire widget on or off."
}
// Привязка клавиши (только отображение ввода — показывает, какая клавиша нажата)
keybind "Highlight Key" {
id: highlightKey
default: input.forward
description: "Which key to highlight in the keystroke display."
}
}Использование настроек в виджете
widget {
opacity: setting(widgetOpacity)
visible: setting(showWidget)
text {
value: setting(customLabel)
fontSize: 11
color: #aaaaaa
}
bar {
value: get(player.health)
max: get(player.maxHealth)
fillColor: setting(healthColor)
width: 100
height: 8
}
}Анимации
Анимации автоматически отключаются, когда у игрока включён reduceMotion (если вы определили этот переключатель). Всегда проверяйте это перед использованием анимаций в виджетах, учитывающих доступность.
text {
value: "⚠ LOW HEALTH"
color: #ff0000
animate {
property: opacity
from: 1.0
to: 0.3
duration: 600 // Миллисекунды
easing: ease-in-out // "linear", "ease-in", "ease-out", "ease-in-out"
loop: true
pingpong: true // Реверс на каждом цикле
paused: setting(reduceMotion)
}
}Переход (при смене состояния)
text {
value: "{get(perf.fps)}"
color: #ffffff
transition {
property: color
duration: 200
easing: ease-out
}
}Руководство по реализации CPS
Этот раздел предназначен для разработчиков модов Hudifine, реализующих источники данных
input.cpsиinput.rcpsна стороне Java.
CPS (клики в секунду) — это значение, которое Minecraft сам по себе не отслеживает. Тебе нужно реализовать счётчик кликов, используя скользящее временное окно.
Как это работает
Отслеживай каждый левый клик (атака) и правый клик (использование) с временной меткой. Чтобы получить текущий CPS, посчитай, сколько кликов произошло за последние 1000 мс.
Рекомендуемая реализация на Java (Fabric + Mixin)
Шаг 1 — Храни временные метки кликов
// In your HudifineDataProvider or equivalent class
private static final List<Long> leftClickTimes = new ArrayList<>();
private static final List<Long> rightClickTimes = new ArrayList<>();
public static void registerLeftClick() {
long now = System.currentTimeMillis();
leftClickTimes.add(now);
// Prune old entries older than 1 second
leftClickTimes.removeIf(t -> now - t > 1000);
}
public static void registerRightClick() {
long now = System.currentTimeMillis();
rightClickTimes.add(now);
rightClickTimes.removeIf(t -> now - t > 1000);
}
public static int getLeftCPS() {
long now = System.currentTimeMillis();
leftClickTimes.removeIf(t -> now - t > 1000);
return leftClickTimes.size();
}
public static int getRightCPS() {
long now = System.currentTimeMillis();
rightClickTimes.removeIf(t -> now - t > 1000);
return rightClickTimes.size();
}Шаг 2 — Подключись к кликам мыши через Mixin
@Mixin(MinecraftClient.class)
public class MouseClickMixin {
@Inject(method = "doAttack", at = @At("HEAD"))
private void onLeftClick(CallbackInfo ci) {
HudifineDataProvider.registerLeftClick();
}
@Inject(method = "doItemUse", at = @At("HEAD"))
private void onRightClick(CallbackInfo ci) {
HudifineDataProvider.registerRightClick();
}
}Шаг 3 — Выведи данные в движок скриптов
Сопоставь input.cps → HudifineDataProvider.getLeftCPS() и input.rcps → HudifineDataProvider.getRightCPS() в своём резолвере источников данных.
Примечания
- Скользящее окно в 1000 мс — это стандарт, используемый Lunar Client, Badlion и большинством PvP-клиентов
doAttackсрабатывает при каждой попытке взмаха, включая промахи. Это соответствует тому, как Lunar считает CPSdoItemUseсрабатывает при правом клике независимо от того, какой предмет в руке, что тоже соответствует ожидаемому поведению- Оба списка очищаются при чтении, поэтому они остаются небольшими даже во время длительных сессий
Полные примеры
Минимальный счётчик FPS
meta {
name: "FPS Counter"
author: "example"
version: "1.0.0"
}
widget {
anchor: top-right
offsetX: -10
offsetY: 10
background: #00000066
padding: 6
borderRadius: 4
text {
value: "{get(perf.fps)} FPS"
color: #ffffff
fontSize: 12
shadow: true
}
}Отображение нажатий клавиш
meta {
name: "Keystrokes"
author: "example"
version: "1.0.0"
}
settings {
color "Active Key Color" { id: activeColor default: #ffffff }
color "Inactive Key Color" { id: inactiveColor default: #ffffff33 }
toggle "Show CPS" { id: showCps default: true }
}
widget {
anchor: bottom-right
offsetX: -10
offsetY: -10
direction: column
gap: 3
group {
direction: row
justify: center
gap: 3
keyIndicator { key: input.forward label: "W" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 }
}
group {
direction: row
gap: 3
keyIndicator { key: input.left label: "A" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 }
keyIndicator { key: input.backward label: "S" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 }
keyIndicator { key: input.right label: "D" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 }
}
group {
direction: row
justify: center
keyIndicator { key: input.jump label: "SPACE" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 width: 66 borderRadius: 8 }
}
group {
direction: row
gap: 3
keyIndicator { key: input.attack label: "LMB" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 width: 40 borderRadius: 8 }
keyIndicator { key: input.use label: "RMB" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 width: 40 borderRadius: 8 }
}
if setting(showCps) {
group {
direction: row
gap: 6
justify: center
text { value: "{get(input.cps)} CPS" color: #aaaaaa fontSize: 10 }
text { value: "{get(input.rcps)} CPS" color: #aaaaaa fontSize: 10 }
}
}
}
---
### Full Status HUD
```hud
meta {
name: "Status HUD"
author: "example"
version: "1.0.0"
description: "Здоровье, голод, броня, опыт и координаты."
}
settings {
toggle "Показывать координаты" { id: showCoords default: true }
toggle "Показывать броню" { id: showArmor default: true }
toggle "Показывать опыт" { id: showXp default: true }
toggle "Режим высокой контрастности" { id: highContrast default: false description: "Более контрастные цвета." }
toggle "Крупный текст" { id: largeText default: false }
color "Цвет здоровья" { id: healthColor default: #ff4444 }
slider "Прозрачность" { id: opacity min: 0.2 max: 1.0 step: 0.05 default: 0.85 }
}
widget {
anchor: bottom-left
offsetX: 10
offsetY: -60
background: #00000066
padding: 8
borderRadius: 6
opacity: setting(opacity)
direction: column
gap: 5
// Health
group {
direction: row
gap: 6
align: center
text { value: "❤" color: setting(healthColor) fontSize: if setting(largeText) then 16 else 11 }
bar {
value: get(player.health)
max: get(player.maxHealth)
width: 80
height: if setting(largeText) then 10 else 7
fillColor: setting(healthColor)
backgroundColor: #ffffff22
borderRadius: 3
}
text {
value: "{floor(get(player.health))}/{floor(get(player.maxHealth))}"
color: if setting(highContrast) then #ffff00 else #ffffff
fontSize: if setting(largeText) then 14 else 10
}
}
// Hunger
group {
direction: row
gap: 6
align: center
text { value: "🍗" color: #ffaa44 fontSize: if setting(largeText) then 16 else 11 }
bar {
value: get(player.hunger)
max: 20
width: 80
height: if setting(largeText) then 10 else 7
fillColor: #ffaa44
backgroundColor: #ffffff22
borderRadius: 3
}
text {
value: "{get(player.hunger)}/20"
color: if setting(highContrast) then #ffff00 else #ffffff
fontSize: if setting(largeText) then 14 else 10
}
}
// XP
if setting(showXp) {
group {
direction: row
gap: 6
align: center
text { value: "✦" color: #55ff55 fontSize: if setting(largeText) then 16 else 11 }
bar {
value: get(player.xpProgress)
max: 1.0
width: 80
height: if setting(largeText) then 10 else 7
fillColor: #55ff55
backgroundColor: #ffffff22
borderRadius: 3
}
text {
value: "Lv {get(player.xpLevel)}"
color: if setting(highContrast) then #ffff00 else #55ff55
fontSize: if setting(largeText) then 14 else 10
}
}
}
// Armor
if setting(showArmor) {
separator { direction: horizontal color: #ffffff22 thickness: 1 length: auto margin: 2 }
group {
direction: row
gap: 4
align: center
icon { item: get(inventory.helmetItem) size: 14 }
icon { item: get(inventory.chestplateItem) size: 14 }
icon { item: get(inventory.leggingsItem) size: 14 }
icon { item: get(inventory.bootsItem) size: 14 }
}
}
// Coordinates
if setting(showCoords) {
separator { direction: horizontal color: #ffffff22 thickness: 1 length: auto margin: 2 }
text {
value: "X {floor(get(world.x))} Y {floor(get(world.y))} Z {floor(get(world.z))}"
color: if setting(highContrast) then #ffff00 else #aaaaaa
fontSize: if setting(largeText) then 13 else 10
}
text {
value: "Facing {get(world.facing)} · {get(world.biome)}"
color: if setting(highContrast) then #ffff00 else #777777
fontSize: if setting(largeText) then 12 else 9
}
}
}
---
*Эта документация описывает HUD Script v1.0. Если у вас есть вопросы или вы хотите внести вклад, делитесь своими скриптами в сообществе.*
</details>
<details>
<summary>Руководство по интеграции модов</summary>
# Hudifine — руководство по интеграции модов
Hudifine предоставляет API поставщиков данных, которое позволяет любому моду на Fabric регистрировать собственные источники данных. После регистрации ваши данные становятся доступны внутри HUDScript через `get()` — точно так же, как встроенные источники вроде `get(perf.fps)` или `get(player.health)`.
---
## Требования
- Fabric Loader `>=0.15.0`
- Minecraft `>=26.1.2`
- Hudifine, установленный как зависимость
---
## 1. Добавьте Hudifine как зависимость
В вашем `build.gradle`:
```groovy
repositories {
maven { url = uri("https://cdn.jsdelivr.net/gh/Pacsy1/hudifine@main/public-maven/") }
}
dependencies {
modImplementation "dev.hudifine:hudifine-api:1.0.0"
}В вашем fabric.mod.json объявите его как обязательную зависимость:
{
"depends": {
"hudifine": ">=1.0.0"
}
}2. Реализуйте поставщик данных
Hudifine предоставляет типизированные интерфейсы поставщиков. Выберите тот, который подходит вашим данным.
Доступные типы поставщиков
| Интерфейс | Возвращаемый тип | Пример использования |
|---|---|---|
IntDataProvider | int | FPS, пинг, количество сущностей |
FloatDataProvider | float | TPS, скорость, температура |
StringDataProvider | String | Название биома, измерение, статус |
BooleanDataProvider | boolean | Бежит ли игрок, на земле ли он |
Пример: поставщик FPS
import dev.hudifine.api.provider.IntDataProvider;
public class FpsProvider implements IntDataProvider {
@Override
public String getId() {
return "mymod.fps"; // через точку, совпадает с вызовом get() в HUDScript
}
@Override
public String getDisplayName() {
return "FPS Counter";
}
@Override
public int getValue() {
return Minecraft.getInstance().getFps();
}
}Соглашение об именовании ID
ID поставщиков используют точечную нотацию, чтобы совпадать с тем, как вызываются источники данных в HUDScript:
mymod.fps → get(mymod.fps)
mymod.ping → get(mymod.ping)
mymod.something → get(mymod.something)ID должны быть уникальными. Дубликаты ID отклоняются при загрузке с предупреждением в логе Hudifine.
3. Зарегистрируйте поставщика через entrypoint
В вашем fabric.mod.json зарегистрируйте класс поставщика под entrypoint hudifine:provider:
{
"entrypoints": {
"hudifine:provider": [
"com.yourmod.FpsProvider"
]
}
}Несколько поставщиков сразу:
{
"entrypoints": {
"hudifine:provider": [
"com.yourmod.FpsProvider",
"com.yourmod.PingProvider",
"com.yourmod.BiomeProvider"
]
}
}Hudifine автоматически находит и загружает всех зарегистрированных поставщиков при запуске. Никакого дополнительного кода инициализации с вашей стороны не требуется.
4. Использование ваших данных в HUDScript
После регистрации ваш поставщик доступен в любом HUDScript через get() — так же, как встроенные источники.
Базовое использование
widget {
anchor: top-right
offsetX: -10
offsetY: 10
background: #00000066
padding: 6
borderRadius: 4
text {
value: "{get(mymod.fps)} FPS"
color: #ffffff
fontSize: 12
shadow: true
}
}С условиями
widget {
anchor: top-right
offsetX: -10
offsetY: 10
background: #00000066
padding: 6
borderRadius: 4
text {
value: "{get(mymod.fps)} FPS"
color: if get(mymod.fps) < 30 then #ff4444 else if get(mymod.fps) < 60 then #ffaa00 else #ffffff
fontSize: 12
shadow: true
}
}С полосой
widget {
anchor: bottom-left
offsetX: 10
offsetY: -10
background: #00000066
padding: 8
borderRadius: 6
direction: column
gap: 4
text {
value: "My Mod Stats"
color: #aaaaaa
fontSize: 10
}
group {
direction: row
gap: 6
align: center
text { value: "FPS" color: #aaaaaa fontSize: 10 }
bar {
value: get(mymod.fps)
max: 260
width: 80
height: 7
fillColor: #55ff55
backgroundColor: #ffffff22
borderRadius: 3
}
text {
value: "{get(mymod.fps)}"
color: #ffffff
fontSize: 10
}
}
}
### С настройками игрока
Данные вашего провайдера работают со всеми возможностями HUDScript, включая API настроек игрока:
```hud
settings {
toggle "Show My Mod Stats" { id: showStats default: true }
color "Stat Color" { id: statColor default: #55ff55 }
}
widget {
visible: setting(showStats)
text {
value: "{get(mymod.fps)} FPS"
color: setting(statColor)
fontSize: 12
shadow: true
}
}5. Необязательно: метаданные провайдера
Реализуйте HudifineProviderMeta, чтобы показывать более подробную информацию об источнике в браузере провайдеров Hudifine прямо в игре:
import dev.hudifine.api.provider.IntDataProvider;
import dev.hudifine.api.provider.HudifineProviderMeta;
public class FpsProvider implements IntDataProvider, HudifineProviderMeta {
@Override
public String getId() { return "mymod.fps"; }
@Override
public String getDisplayName() { return "FPS Counter"; }
@Override
public int getValue() { return Minecraft.getInstance().getFps(); }
// Optional metadata
@Override
public String getDescription() {
return "Current rendered frames per second.";
}
@Override
public String getCategory() {
return "performance"; // Groups it with perf.fps, perf.tps etc. in the browser
}
@Override
public String getUnit() {
return "fps";
}
}6. Рекомендации по производительности
getValue() вызывается каждый кадр. Сделайте его быстрым:
- Кэшируйте дорогие вычисления и обновляйте их по тику, а не каждый кадр
- Избегайте создания объектов внутри
getValue(), где возможно используйте примитивы - Если ваши данные меняются только при определённых событиях, обновляйте поле по этому событию и возвращайте закэшированное поле
Хороший пример:
public class EntityCountProvider implements IntDataProvider {
private int cachedCount = 0;
@SubscribeEvent
public void onTick(TickEvent event) {
// Runs 20x/sec instead of every frame
this.cachedCount = world.getEntities().size();
}
@Override
public String getId() { return "mymod.entityCount"; }
@Override
public String getDisplayName() { return "Nearby Entity Count"; }
@Override
public int getValue() {
return cachedCount; // Just a field read - instant
}
}7. Публикация в магазине
Вероятно, стоит включить Hudifine в название вашего мода — для этого просто начните название мода с 'Hudifine' для лучшей находимости.
8. Создание HUD с вашими данными
Это руководство охватывает только сторону интеграции мода. Чтобы узнать, как на самом деле писать виджеты HUDScript, использующие данные вашего провайдера, элементы, разметку, стилизацию, условия, анимации, настройки и многое другое, обратитесь к документации для разработчиков HUDScript выше.
Итог
| Шаг | Что делать |
|---|---|
| 1 | Добавьте Hudifine API как зависимость |
| 2 | Реализуйте типизированный интерфейс провайдера с ID в точечной нотации |
| 3 | Зарегистрируйте его в fabric.mod.json в разделе hudifine:provider |
| 4 | Пользователи получают доступ к вашим данным через get(yourid) в HUDScript |
| 5 | (Необязательно) Реализуйте HudifineProviderMeta для браузера прямо в игре |
| 6 | Держите getValue() быстрым — он выполняется каждый кадр |
| 7 | (Необязательно) Опубликуйте мод на Modrinth, включив Hudifine в его название |
| 8 | Смотрите документацию для разработчиков HUDScript, чтобы создавать настоящие виджеты |
Hudifine Mod Integration Guide - v1.0
Частые вопросы
Как установить Hudifine?
Установите Fabric под свою версию Minecraft, скачайте Hudifine под ту же версию и загрузчик, положите файл в папку mods и запустите игру с профилем загрузчика.
Для каких версий Minecraft есть Hudifine?
У Hudifine есть сборки для Minecraft 26.1.2–26.2.
Нужен ли Hudifine на сервере?
Ставится только в игру; на сервер ставить не нужно