BLOXAR Lua API

Скрипты блоков в BLOXAR пишутся на Lua и выполняются в изолированной виртуальной машине (fengari) — у каждого скрипта своё собственное состояние, скрипты не видят переменные друг друга напрямую. Общаться между собой блоки могут только через явные функции движка, описанные в этом документе, или через события (emit/on).

🔑 Главный принцип

Скрипт не имеет прямого доступа к 3D-объектам движка. Вместо parent.position.x = 5 используются функции вида setBlockPosition(id, x, y, z), где id — уникальный UUID блока.

🚀 Первый скрипт

  1. Откройте проект в Studio Mode.
  2. В панели Explorer выберите блок, нажмите и добавьте 📜 Script.
  3. Кликните по скрипту — откроется редактор кода.
  4. Напишите Lua-код и нажмите Save & Close.
  5. Нажмите Publish, чтобы опубликовать мир на сервер.

Важно: скрипты сохраняются только через Publish (реальная публикация мира), а не через локальное сохранение .blxr-файла. После публикации перезайдите в место, чтобы скрипт применился.

Минимальный пример

function onTick(dt)
    if isKeyDown('e') then
        say('Привет из скрипта!')
    end
end

onTick(dt) — единственная функция, которую движок вызывает автоматически, каждый кадр, с dt — временем в секундах с прошлого кадра. Весь остальной код внутри скрипта пишется вокруг неё.

🧬 Переменные блока

Эти функции доступны только внутри скрипта, привязанного к блоку — они возвращают данные именно этого блока, не требуя UUID.

getBlockX() / getBlockY() / getBlockZ()→ number

Текущие координаты блока, на котором висит скрипт.

getBlockName()→ string

Имя блока, заданное в редакторе.

getBlockUUID()→ string

Уникальный ID этого блока — используй его в функциях вроде setBlockPosition(id, ...), чтобы двигать сам себя.

⚡ События

onPlayerEnter(function(who) ... end)

Вызывается один раз, когда игрок заходит в радиус ~1.5 от блока. who — ник игрока.

onTouch(function(who) ... end)

Вызывается один раз, когда игрок вплотную касается блока (радиус ~1.0).

emit(eventName, key1, val1, key2, val2, ...)

Отправляет событие всем блокам, подписанным на eventName через on().

on(eventName) / off(eventName)

Подписка/отписка. При срабатывании вызывается глобальная функция скрипта onEvent_<eventName>(data).

-- Блок-кнопка
on('door_toggle')
function onEvent_door_toggle(data)
    say('Дверь получила сигнал от ' .. data.from)
end

function onTick(dt)
    if isKeyDown('e') then
        emit('door_toggle', 'state', 'open')
    end
end

❤️ Здоровье и позиция игрока

getPlayerHealth() / getPlayerMaxHealth()→ number

Текущее и максимальное здоровье игрока.

isPlayerAlive() / isPlayerDead()→ boolean

Жив ли игрок сейчас.

damagePlayer(amount) / healPlayer(amount)

Наносит урон / восстанавливает здоровье. Не выходит за пределы 0..max.

killPlayer() / respawnPlayer()

Мгновенно убивает или возрождает игрока на точке спавна.

isPlayerSpawnProtected()→ boolean / protectPlayer()

Проверка/включение временной неуязвимости после спавна.

getPlayerPosition()→ x, y, z

Позиция игрока. Возвращает три числа: local x, y, z = getPlayerPosition().

setPlayerPosition(x, y, z) / movePlayerBy(dx, dy, dz)

Телепортирует игрока или сдвигает его на смещение.

teleportPlayerToBlock(uuid)

Телепортирует игрока к указанному блоку (чуть выше его позиции).

getPlayerRotationY()→ number / setPlayerRotationY(rad)

Поворот игрока вокруг вертикальной оси, в радианах.

getPlayerNickname() / getPlayerSkin()

Ник и скин локального игрока.

isNoclipEnabled()→ boolean / setNoclip(enabled)

Режим прохождения сквозь стены.

distanceToPlayer(x, y, z)→ number

Расстояние от игрока до точки — основа для ловушек и триггеров.

⌨️ Ввод

isKeyDown(key)→ boolean

Зажата ли клавиша сейчас. Строка в нижнем регистре: 'w', 'e', ' ' (пробел), 'shift'.

isAnyKeyDown(...keys) / areAllKeysDown(...keys)→ boolean

Хотя бы одна / все перечисленные клавиши зажаты одновременно.

getPressedKeys()→ string

Список всех зажатых клавиш через запятую — для отладки.

🎥 Камера

getCameraPosition()→ x, y, z

Текущая позиция камеры.

getCameraDistance()→ number / setCameraDistance(dist)

Дистанция камеры от игрока в режиме третьего лица (1..40).

isStudioMode()→ boolean

Выполняется ли скрипт в режиме студии (там события касания не срабатывают).

🌍 Время суток и гравитация

getDayTime()→ number / setDayTime(radians)

Текущее время суток в радианах (0..2π — полный цикл).

setTimeOfDay('day'|'night'|'dawn'|'dusk')

Быстрая установка времени суток по названию.

getGravity()→ number / setGravity(value) / resetGravity()

Читает/меняет силу гравитации в мире, resetGravity() возвращает значение по умолчанию.

getEnvironmentData() / getWorldData()

Служебные данные об окружении и мире.

toggleClouds(enabled) / generateTerrain()

Включает/выключает облака, либо запускает генерацию рельефа.

scanArea()→ string

Сканирует размещённые блоки (вода/лава рядом, средняя высота) — полезно для AI-скриптов. Формат: "hasWater:0,hasLava:0,blockCount:12,avgY:3.5,dominantColor:...".

🧱 Блоки

Каждая функция принимает или возвращает uuid — уникальный ID блока (получить его можно через getBlockUUID() у своего блока, findBlockByName() у чужого, или как результат spawnBlock()).

Поиск

findBlockByName(name)→ string | nil

UUID первого блока с указанным именем.

findAllBlocksByName(name)→ string

Все совпавшие UUID через запятую. См. также таблицы данных ниже.

getUniqueBlockNames()→ string / blockExists(uuid)→ boolean / getBlockCount()→ number

Общая информация по всем блокам в мире.

Чтение свойств

getBlockPosition(uuid)→ x, y, z / getBlockScale(uuid)→ x, y, z

Позиция и масштаб блока.

getBlockName(uuid) / getBlockColor(uuid) / isBlockPhysical(uuid)

Имя, цвет (hex) и физическая коллизия блока.

Изменение

setBlockPosition(uuid, x, y, z) / moveBlockBy(uuid, dx, dy, dz)

Двигает блок в мире.

setBlockColor(uuid, hex) / setBlockScale(uuid, sx, sy, sz) / setBlockRotation(uuid, rx, ry, rz)

Перекрашивает, масштабирует, вращает блок (радианы).

setBlockVisible(uuid, visible)

Показывает/прячет блок.

Создание / удаление

spawnBlock(shape, color, x, y, z, sx, sy, sz)→ string (uuid)

Создаёт новый блок. shape: 'box', 'sphere', 'cone', 'cylinder'.

removeBlock(uuid) / removeAllBlocksByName(name)

Удаляет блок (или все блоки с указанным именем).

📡 Рейкаст

raycastForward(maxDistance)→ string (uuid) | nil

Луч из камеры вперёд. Возвращает UUID первого попавшегося блока.

raycastFrom(ox, oy, oz, dx, dy, dz, maxDistance)→ string (uuid) | nil

Луч из произвольной точки в произвольном направлении.

🎒 Инвентарь и очки

getActiveSlot()→ number / selectSlot(slotNum)

Текущий выбранный слот и переключение на другой.

getInventoryItem(slotNum) / hasItemInSlot(slotNum)

Что лежит в слоте.

giveItem(slot, itemId) / clearInventorySlot(slot)

Кладёт предмет в слот или очищает его.

setGlobalVar(key, value) / getGlobalVar(key)

Переменные, общие для всех скриптов на время сессии — удобно для счёта очков или простой валюты.

setGlobalVar('score', (getGlobalVar('score') or 0) + 10)
say('Очки: ' .. getGlobalVar('score'))

👥 Мультиплеер

Только чтение — другие игроки управляются сервером напрямую.

getOtherPlayers()→ string

Формат: "id:nick:x:y:z;id:nick:x:y:z". Для нормальной таблицы см. getOtherPlayersTable() ниже.

getOtherPlayerCount()→ number / getOtherPlayerPosition(id)→ x, y, z

Количество игроков рядом и позиция конкретного по его id.

🔊 Звук и чат

playSound(type, intensity)

Проигрывает звуковой эффект движка.

playMusic(url, name) / stopMusic()

Фоновая музыка по ссылке.

say(text)

Сообщение от лица скрипта в чат + пузырь речи над игроком.

announce(text)

Системное сообщение другим цветом — для предупреждений.

⏱ Таймеры

wait(seconds, callback)

Выполняет Lua-функцию через указанное количество секунд.

wait(2, function()
    say('Прошло 2 секунды')
end)

🖥️ UI и диалоги

showNotification(text, duration)

Всплывающее уведомление вверху экрана (toast).

showDialog(title, text)

Модальное окно с кнопкой OK по центру экрана.

📊 Таблицы данных

Большинство функций API возвращают строку (проще маршалить в Lua), но для списков объектов есть версии, отдающие настоящую Lua-таблицу.

getOtherPlayersTable()→ table

Массив таблиц {id, nickname, x, y, z}.

findAllBlocksByNameTable(name)→ table

Массив UUID-строк вместо строки через запятую.

for i, p in ipairs(getOtherPlayersTable()) do
    print(p.nickname, p.x, p.y, p.z)
end

🎬 Анимации

registerAnimation(uuid, speedY) / unregisterAnimation(uuid)

Включает/выключает встроенную анимацию блока (например, постоянное вращение).

🧮 Математика и утилиты

distance3D(x1,y1,z1, x2,y2,z2)→ number

Расстояние между двумя точками в 3D.

clamp(v, min, max) / lerp(a, b, t) / randomRange(min, max) / vectorLength(x, y, z)

Стандартные хелперы, которых не хватает в чистом Lua.

getTime()→ number

Время в секундах с момента загрузки страницы.

print(...) / warn(...)

Вывод в консоль браузера — основной инструмент отладки скриптов.

💡 Готовые примеры (Copy & Paste)

Лёгкая машина (сесть по E)

local carId = nil
local seated = false
local eWasDown = false
local speed = 6.0
local turnSpeed = 2.2
local enterRadius = 2.5
local rotY = 0

function onTick(dt)
    if carId == nil then carId = getBlockUUID() end
    local cx, cy, cz = getBlockX(), getBlockY(), getBlockZ()
    local eDown = isKeyDown('e')
    local ePressed = eDown and not eWasDown
    eWasDown = eDown

    if not seated then
        if ePressed and distanceToPlayer(cx, cy, cz) < enterRadius then
            seated = true
            say('Сел за руль')
        end
        return
    end

    if ePressed then
        seated = false
        say('Вышел из машины')
        setPlayerPosition(cx + 2, cy + 1, cz)
        return
    end

    if isKeyDown('a') then rotY = rotY + turnSpeed * dt end
    if isKeyDown('d') then rotY = rotY - turnSpeed * dt end

    local moveDir = 0
    if isKeyDown('w') then moveDir = 1 end
    if isKeyDown('s') then moveDir = -1 end

    local nx, nz = cx, cz
    if moveDir ~= 0 then
        nx = cx + math.sin(rotY) * speed * dt * moveDir
        nz = cz + math.cos(rotY) * speed * dt * moveDir
    end

    setBlockPosition(carId, nx, cy, nz)
    setBlockRotation(carId, 0, rotY, 0)
    setPlayerPosition(nx, cy + 1.0, nz)
    setPlayerRotationY(rotY)
end

Ловушка-шипы (урон при касании)

local cooldown = 0

function onTick(dt)
    cooldown = math.max(0, cooldown - dt)
    local x, y, z = getBlockX(), getBlockY(), getBlockZ()
    if distanceToPlayer(x, y, z) < 1.2 and cooldown <= 0 then
        cooldown = 1.5
        damagePlayer(20)
        playSound('land', 1)
    end
end

Интерактивная кнопка

local activated = false

function onTick(dt)
    local x, y, z = getBlockX(), getBlockY(), getBlockZ()
    if distanceToPlayer(x, y, z) < 3 and not activated then
        if isKeyDown('e') then
            activated = true
            playSound('jump', 1)
            setBlockColor(getBlockUUID(), 0x2EE6A6)
            announce('Кнопка активирована!')
        end
    end
end

Портал с задержкой

local used = false

function onPlayerEnter(who)
    if used then return end
    used = true
    say(who .. ' активировал портал')
    wait(1, function()
        setPlayerPosition(0, 5, 0)
        used = false
    end)
end