import React from 'react';
import DocIcon from './docsIcons';
import { LangTabs } from './docsLang';
/**
* docsData.jsx — контент вики редактора Рублокс (разделы A-J).
*
* Структура: DOCS = массив глав. Каждая глава —
* { id, icon, title, summary, sections: [{ id, title, body }] }
* - icon — имя SVG-иконки из docsIcons.jsx (НЕ эмодзи).
* - summary — короткое описание для карточки на главной вики.
*
* Хелперы для оформления:
* -
Рублокс — это платформа, где можно играть в 3D-игры
и создавать свои собственные. Всё работает прямо в браузере:
ничего скачивать и устанавливать не нужно.
Редактор игр (его ещё называют Studio) — это место,
где ты строишь игру. Ты ставишь блоки и модели, рисуешь
кнопки, пишешь скрипты — а потом нажимаешь «Играть»
и сразу проверяешь, что получилось.
В Рублоксе можно сделать почти любую игру:
Как устроена эта вика. Разделы A-C научат строить
мир и интерфейс. Разделы D-G — писать скрипты (код, который
оживляет игру). Раздел H — справочник всех команд. Раздел I —
словарик непонятных слов. Раздел J — что делать, если
что-то сломалось. Раздел K — 50 готовых игр-уроков.
Когда ты открываешь игру в редакторе, экран делится
на части. Разберём каждую:
Когда ты выбираешь инструмент «Блок» или «Примитив»,
слева открывается ещё одна палитра — там лежат
фигуры, которые можно ставить на сцену.
Как двигать камеру в редакторе:
Соберём самую простую игру — площадку, по которой можно
ходить. Делай по шагам.
Поздравляем — это уже работающая игра. Дальше ты добавишь
в неё препятствия, врагов, монетки и логику.
На вкладке Главная есть инструменты создания:
Когда выбираешь «Блок» или «Примитив», слева открывается
палитра — выбери в ней фигуру, а потом кликай
по сцене, чтобы её поставить.
Шаг привязки (1.0 / 0.5 / 0.25 / Выкл) — задаёт,
насколько мелко объект «прилипает» к сетке, когда ты его
двигаешь. Шаг 1.0 — объект двигается крупными шагами,
ровно по клеткам. Маленький шаг 0.25 — точнее, но дольше.
Гизмо — это цветные стрелки и кольца, которые
появляются на выделенном объекте. Они помогают точно
его двигать, поворачивать и менять размер.
Режим гизмо выбирается в ленте инструментов — группа
«Манипуляторы»:
Вот как выглядит гизмо «Двигать» на выделенном кубе —
три цветные стрелки:
Оси всегда одни и те же и покрашены одинаково:
Иерархия — это список всех объектов твоей игры
в правой панели. Когда сцена большая, найти нужный куб
мышкой трудно — а в списке он всегда под рукой.
Объекты сгруппированы по категориям:
Имя объекта. У каждого объекта есть имя — его видно
в иерархии и можно изменить в инспекторе. Имена очень важны
для скриптов: команда
Папки. Объекты можно складывать в свои папки —
например, папка «Уровень 1», папка «Враги». Это как
наводить порядок в шкафу: всё на своих полках. Двойной
клик по объекту в списке — камера прилетит прямо к нему.
Горячие клавиши экономят кучу времени:
Кнопка Запустить в правой части
ленты запускает игру. Сцена «оживает»: включается физика,
начинают работать скрипты, появляется HUD (счётчики поверх
экрана). Чтобы остановить игру — кнопка
Стоп или клавиша
Esc.
Управление в игре:
HP (здоровье) игрока видно в левом верхнем углу.
Когда игрок получает урон, полоска краснеет. Если HP падает
до нуля — игрок «умирает» и через пару секунд
воскресает (респаун) на точке спавна с полным
здоровьем.
Всем этим можно управлять из скриптов: наносить урон,
лечить, ставить чекпоинты. Об этом — раздел F1.
Сохранение — кнопка Сохранить
или Ctrl+S.
Игра автоматически сохраняется и сама время от времени,
но лучше не лениться и сохранять руками.
Публикация — когда игра готова, нажми
Опубликовать. Выбери
возрастной рейтинг (6+, 12+, 16+, 18+) и куда отправить:
После отправки игру проверит модератор (обычно за 24-48
часов). Игра не должна содержать матов, рекламы,
чужого контента (модели из Roblox/Minecraft) и жестокости
не по возрасту.
Блок — это кубик одного размера. Блоки ровно
встают по сетке, и из них удобно строить — как из кубиков
Лего. В палитре слева есть разные типы: трава, камень,
песок, дерево, кирпич, цветные блоки, блоки со снегом.
Чтобы поставить блок — выбери инструмент
Блок, кликни нужный тип
в палитре и кликай по сцене. Чтобы убрать — инструмент
Стереть.
У блока в инспекторе можно включить/выключить
столкновение (твёрдый он или сквозь него можно
пройти) и видимость.
Примитив — простая 3D-фигура. В отличие от блока,
у примитива можно свободно менять размер по каждой оси
и красить в любой цвет. Виды:
Примитивы — главный «строительный материал» для игр.
Из растянутых кубов делают платформы для паркура,
из сфер — собираемые монетки, из конусов — смертельные
шипы.
Модель — готовая красивая 3D-фигура: дерево, камень,
бочка, машина, оружие, мебель. Их не нужно строить из
кубиков — просто бери из библиотеки и ставь на сцену.
Инструмент Модель открывает
каталог. Модели разбиты по категориям (природа, город,
оружие...).
Свои модели. Можно загрузить собственную 3D-модель
в формате
Ландшафт — это рельеф мира: холмы, горы, ямы, пещеры.
В Рублоксе два режима:
Инструменты лепки: поднять, опустить,
разгладить, покрасить. Есть и кисти-растения —
рисуешь по земле, и сразу вырастают деревья и трава.
Точка спавна — место, где игрок появляется в начале
игры и куда возвращается после смерти. Ставится на вкладке
Игра → Ставить спавн. Точка спавна одна.
Чекпоинт (контрольная точка) — промежуточное место
сохранения в длинных уровнях. Когда игрок дошёл до чекпоинта,
при следующей смерти он воскреснет уже там, а не в самом
начале.
Чекпоинт делают скриптом на флажке-объекте: когда игрок
касается флажка, скрипт запоминает это место как новый спавн.
Что тут происходит:
Лампа — это источник света. Кроме общего солнца,
можно поставить точечные лампы, которые освещают всё рядом
с собой. Они нужны для пещер, ночных уровней, подсветки
важных мест.
У лампы настраиваются цвет, яркость
и радиус (как далеко достаёт свет). Лампу можно
создать и из скрипта:
Частицы — это много маленьких летящих точек:
искры, дым, огонь, магия. Объект, который их создаёт,
называется эмиттер.
Частицы делают игру живой: костёр дымит, при победе летит
конфетти, у портала кружится магия. Из скрипта это команда
Типы эффектов:
Триггер — невидимая зона, которая что-то запускает,
когда игрок в неё входит. Например: игрок зашёл в зону —
открылась дверь, заиграла музыка, появился враг.
Как сделать триггер:
Когда ты выделяешь объект, в Инспекторе (правая
панель снизу) появляются его свойства:
GUI (читается «гуи») — это интерфейс игры:
всё, что нарисовано поверх 3D-сцены. Кнопки, надписи,
счётчик очков, полоска здоровья, меню — это всё GUI.
GUI не находится внутри игрового мира — он «приклеен»
к экрану. Куда бы ни смотрел игрок, кнопка остаётся
на том же месте экрана.
В редакторе есть визуальный редактор UI: ты
перетаскиваешь элементы мышкой, ставишь их в нужное место,
меняешь цвет и текст — и сразу видишь результат. Открыть
его можно через инструмент Интерфейс.
Из чего собирается интерфейс:
Имя элемента. Как и у объектов сцены, у GUI-элемента
есть имя. Скрипт находит элемент по имени командой
Нарисованная кнопка сама по себе ничего не делает —
нужен скрипт.
— код-блок (тёмный, моноширинный)
* - }
lua={
);
// ── Плашка «куда писать скрипт» ───────────────────────────────────
// kind="global" — глобальный скрипт (создаётся в категории «Скрипты»)
// kind="object" — скрипт привязан к объекту (передай on="название объекта")
export const ScriptKind = ({ kind, on }) => {
if (kind === 'object') {
return (
{children}game.self — это и есть твой объект.
{caption &&
Правая кнопка мыши + движение осмотреться по сторонам WASD лететь вперёд / влево / назад / вправо Колесо мыши приблизить / отдалить
.glb.
Выделить обычный режим, клик выбирает объект Двигать три стрелки X / Y / Z — тяни, объект едет по оси Вращать три кольца — тяни, объект поворачивается Масштаб кубики на осях — тяни, объект растягивается
{`{ x: 5, y: 2, z: 0 }`} — это точка
в мире: 5 вправо, 2 вверх, 0 вперёд.
game.scene.findOne('Дверь')
находит объект по имени. Давай объектам понятные
имена: «Дверь», «Монетка1», «Босс».
Ctrl+S Сохранить игру Ctrl+Z Отменить последнее действие Ctrl+D Дублировать выделенный объект Del Удалить выделенное R Повернуть объект на 90° Esc Снять выделение / выйти из режима F Навести камеру на выделенное
WASD Идти Space Прыжок Shift Бег Мышь Поворот камеры ЛКМ Атака / стрельба E Взаимодействовать 1…5 Слот инвентаря C 1-е / 3-е лицо
Куб стены, платформы, ящики Сфера мячи, монетки, планеты Цилиндр колонны, бочки, трубы Конус шипы, ёлки, шляпы Плоскость тонкий лист — пол, экран Тор кольцо (как бублик) Клин наклонная фигура — пандус game.scene.findOne('Платформа1').
.glb. Такие файлы делают в бесплатных
редакторах вроде Blender или берут на сайтах с бесплатными
моделями. После загрузки твоя модель появится в палитре
рядом с остальными.
game.scene.surfaceY(x, z).
{`// Когда игрок коснётся флажка —
// это место станет новой точкой возрождения.
game.self.onTouch(() => {
// game.self.position — координаты самого флажка
game.player.setSpawn(game.self.position);
game.ui.showText('Чекпоинт сохранён!', 1.5);
game.sound.play('pickup');
});`}
onTouch срабатывает,
когда игрок дотронулся до флажка. setSpawn
запоминает точку возрождения. showText
показывает надпись на 1.5 секунды. Готово — игрок
не начнёт уровень заново.
{`// Создаём тёплую лампу над сценой
game.scene.spawn('light:point', {
x: 0, y: 4, z: 0, // где висит лампа
color: '#ffdd88', // тёплый жёлтый свет
brightness: 2, // яркость
range: 12 // радиус освещения
});`}
>
),
},
{
id: 'particles',
title: 'B7. Эмиттеры частиц',
body: (
<>
game.scene.spawnParticles:
{`// Залп конфетти над центром сцены
game.scene.spawnParticles(
'confetti', // тип эффекта
{ x: 0, y: 3, z: 0 }, // где появятся частицы
{ duration: 2, count: 3 } // длительность и густота
);`}
fire (огонь),
smoke (дым), sparks (искры),
magic (магия), explosion (взрыв),
confetti (конфетти).
{`// Игрок вошёл в зону — показываем надпись
game.self.onTouch(() => {
game.ui.showText('Ты вошёл в опасную зону!', 2);
});
// Игрок вышел из зоны
game.self.onUntouch(() => {
game.ui.showText('Ты в безопасности', 1.5);
});`}
>
),
},
{
id: 'object-properties',
title: 'B9. Свойства объекта: цвет, материал, физика, замок',
body: (
<>
Цвет закрасить примитив в любой цвет Материал обычный, металл, стекло, неон — как объект блестит Текстура наложить свою картинку на поверхность Столкновение твёрдый объект или сквозь него можно пройти Видимость показать или спрятать объект Закреплён если выключить — объект падает под действием физики Замок (Lock) заблокировать, чтобы случайно не сдвинуть game.ui.score,
game.ui.timer), которые рисует сама игра
по команде из скрипта.
game.gui.find('Кнопка старта'). Давай
кнопкам и надписям понятные имена.
{`-- Скрипт висит на кнопке (TextButton)
-- script.Parent — это сама кнопка.
local btn = script.Parent
btn.MouseButton1Click:Connect(function()
print("Кнопка нажата!")
end)`}}
/>
Можно и наоборот — управлять кнопкой из глобального скрипта:
{`local Players = game:GetService("Players")
local player = Players.LocalPlayer
local gui = player:WaitForChild("PlayerGui")
-- Находим кнопку по имени (она лежит где-то в PlayerGui)
local btn = gui:FindFirstChild("Кнопка старта", true)
btn.MouseButton1Click:Connect(function()
print("Игра началась!")
btn.Visible = false -- спрятать кнопку
end)`}}
/>
JS: game.gui.find ищет элемент по имени.
game.gui.onClick вешает действие, game.gui.hide прячет.
Lua: gui:FindFirstChild(name, true) ищет рекурсивно
(третий аргумент true = во вложенных).
MouseButton1Click — стандартный сигнал клика на TextButton.
btn.Visible = false прячет элемент.
Поле ввода позволяет игроку напечатать ответ. Когда он нажмёт Enter, скрипт получает введённый текст.
{`local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local player = Players.LocalPlayer
local gui = player:WaitForChild("PlayerGui")
-- Находим TextBox по имени
local box = gui:FindFirstChild("Поле кода", true)
-- FocusLost срабатывает когда игрок нажал Enter или ушёл с поля.
-- Первый параметр enterPressed = true только если был Enter.
box.FocusLost:Connect(function(enterPressed)
if not enterPressed then return end
if box.Text == "1234" then
print("Верно! Дверь открыта")
local door = workspace:WaitForChild("Дверь")
local goal = { Position = door.Position + Vector3.new(0, 8, 0) }
TweenService:Create(door, TweenInfo.new(1), goal):Play()
else
print("Неверный код")
end
end)`}}
/>
JS-разбор: onSubmit даёт переменную
text — то, что напечатал игрок.
if (text === '1234') — проверяем код.
Lua-разбор: на TextBox сигнал FocusLost
срабатывает когда поле теряет фокус (Enter или клик мимо).
Текст лежит в box.Text.
"1234" означают, что это
текст, а не число. Игрок печатает в поле всегда
текст, поэтому и сравнивать нужно с текстом.
У каждого GUI-элемента в инспекторе настраивается внешний вид: цвет фона и прозрачность, граница (рамка, её цвет и толщина), скругление углов (большое скругление делает кнопку «таблеткой»), тень (мягкая тень под элементом), цвет и размер текста.
В элемент Картинка можно загрузить своё изображение с компьютера (PNG или JPG): логотип игры, иконку кнопки, фон главного меню.
В Рублоксе можно писать скрипты на двух языках: JavaScript и Lua. Оба работают одинаково хорошо. Игра не отличает их между собой — внутри одного проекта одни скрипты могут быть на JS, другие на Lua, и они общаются между собой как будто это один язык.
Чем они отличаются?
| JavaScript | Lua | |
|---|---|---|
| Где ещё используется | Сайты, мобильные приложения, серверы. Самый популярный язык в мире. | Roblox, World of Warcraft (моды), многие игры. Простой и быстрый. |
| Главный API | game.* (game.player,
game.log, game.scene) |
game.* в Roblox-стиле
(game:GetService("Players"), workspace) |
| Похож на | Roblox-LUA если знаешь Roblox | Roblox-Studio — те же команды |
| Когда выбрать | Если планируешь делать сайты и приложения — JS пригодится везде. | Если играешь в Roblox и видел там скрипты — Lua тебе знаком. |
Один и тот же пример на двух языках:
Когда игрок касается синего блока — печатаем «Привет».
{`-- Lua — скрипт на самом блоке
local part = script.Parent
part.Touched:Connect(function(hit)
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if player then
print("Привет, " .. player.Name .. "!")
end
end)`}}
/>
Видишь — оба варианта делают одно и то же. Но
запись отличается. JS короче для простых вещей через
game.onTouch, Lua даёт точный Roblox-стиль
через :Connect и события.
Что выбирать новичку?
game.* в JS короче и проще читать.
Большая часть уроков в этой вике написана с примерами
на JS — все они работают, просто копируй.
game:GetService,
:Connect, workspace,
script.Parent.
Можно ли менять язык в одном скрипте?
Да. В редакторе скрипта вверху есть две кнопки — JS и Lua. Просто нажми на нужную. Твой код на текущем языке сохранится, а на другом языке откроется пустой шаблон или то, что ты писал там раньше. Никогда ничего не теряется.
А что под капотом?
JS-скрипты исполняются в WebWorker —
это отдельный поток в браузере, чтобы скрипт не
тормозил саму игру. Lua-скрипты исполняются через
wasmoon — Lua-интерпретатор, скомпилированный
в WebAssembly. Оба варианта работают на любом
устройстве без установки.
Скрипт — это набор команд, который оживляет игру. Блоки и модели сами по себе просто стоят. Чтобы монетка собиралась, дверь открывалась, а враг гнался за игроком — нужен скрипт.
Скрипты пишут на одном из двух языков: JavaScript или Lua. Оба работают одинаково. Подробнее про выбор языка — статья D0 выше. В этом уроке покажем пример на обоих языках — переключай вкладки, чтобы видеть нужный.
Как создать первый скрипт:
{`print("Привет! Игра запустилась.")`}}
/>
Это твой первый работающий скрипт.
game.log(...) печатает в консоль.
Каждая команда в JavaScript заканчивается точкой с запятой
; — как точка в предложении. Текст пишут
в кавычках: 'привет'.
}
lua={print(...) печатает в консоль.
В Lua точку с запятой ставить не нужно.
Текст пишут в кавычках: "привет" или
'привет'. Lua использует ..
(две точки) для склейки текста: "А=" .. 5.
Это очень важно понять с самого начала. Скрипты бывают двух видов, и в каждом уроке вики написано, какой именно нужен.
Глобальный скрипт — это «мозг игры». Он не привязан ни к чему. Запускается один раз при старте. В нём пишут общие правила: подсчёт очков, таймер уровня, проверку победы.
Скрипт на объекте относится к конкретному кубу, модели или кнопке. Внутри такого скрипта работает волшебное слово (своё для каждого языка):
{`// JS: game.self — это и есть тот объект, на котором висит скрипт
game.self.onClick(() => {
game.log('Кликнули по мне!');
});`}
>}
lua={<>
{`-- Lua: script.Parent — это и есть тот объект, на котором висит скрипт
local part = script.Parent
part.Touched:Connect(function(hit)
print("Касание объекта " .. part.Name)
end)`}
>}
/>
Как привязать скрипт к объекту: выдели объект на сцене, потом создай скрипт — он автоматически привяжется к выделенному объекту. Или укажи носителя в настройках скрипта.
game.self — это скрипт на объекте.
Если game.self нет — скрипт глобальный.
Плашка в начале каждого урока всегда подскажет.
}
lua={ script.Parent — это скрипт на объекте.
Если только game — глобальный.
В Lua глобальные скрипты обычно работают со списком
игроков: game:GetService("Players").
Переменная — это «коробочка с именем», в которой скрипт хранит значение. Например, количество очков, имя игрока, выбранный уровень.
{`-- Lua: создаём переменную через local
local score = 0
-- Меняем значение
score = score + 10 -- теперь в score лежит 10
score = score + 5 -- теперь 15
print("Очков:", score) -- напечатает: Очков: 15`}}
/>
let — это слово «создать переменную». Пишут
его только один раз, когда коробочку заводят. Дальше
меняют значение уже без let.
}
lua={
local — это слово «создать переменную внутри
скрипта». Пишут его только один раз. Если опустить
local — переменная станет глобальной (доступна
всем скриптам), это редко нужно.
В переменную можно класть не только числа:
{`local name = "Герой" -- текст — в кавычках
local isWin = false -- да/нет — true или false
local coinCount = 0 -- число — без кавычек`}}
/>
let можно писать const
(«постоянная»). Например, найденную один раз дверь:
const door = game.scene.findOne('Дверь');
}
lua={local. Если хочешь явно показать
что значение не меняется, пиши имя ЗАГЛАВНЫМИ:
local DOOR = workspace.Дверь.
Это договорённость, а не правило Lua.
В каждом скрипте есть одно главное волшебное слово —
game. Через него ты управляешь всей игрой.
Но «отделы» у JS и Lua разные:
game.player | управление игроком |
game.scene | объекты сцены |
game.ui | счётчики и текст на экране |
game.gui | кнопки и меню |
game.sound | звуки |
game.physics | лучи, импульсы, взрывы |
game.self | объект-носитель скрипта |
Запись через точку читается слева направо.
game.player.teleport(0, 5, 0) читается так:
«у игры, у игрока, выполни телепорт
в точку 0, 5, 0».
workspace | 3D-объекты сцены |
game:GetService("Players") | список игроков |
game.Workspace | то же что workspace |
script.Parent | объект-носитель скрипта |
game:GetService("RunService") | каждый кадр (Heartbeat) |
game:GetService("UserInputService") | клавиши и мышь |
game:GetService("TweenService") | плавные движения |
Знак : (двоеточие) в Lua — это вызов
метода объекта. game:GetService("Players")
читается так: «у game вызови GetService
и дай ему текст Players».
Точка . — это доступ к полю объекта.
workspace.Floor.BrickColor — у workspace
взять Floor, у него взять BrickColor.
Полный список всех команд — в Справочнике (раздел H). Не нужно его заучивать: при наборе кода редактор сам показывает подсказки.
> ), }, { id: 'log-console', title: 'D5. log/print, консоль, отладка', body: ( <>Консоль — окошко в правом нижнем углу редактора. Туда выводятся все сообщения и ошибки скриптов.
game.log(...) печатает в консоль
что угодно. Это главный инструмент отладки —
проверки, что код работает правильно:
}
lua={
Команда print(...) печатает в консоль
что угодно. Это главный инструмент отладки —
проверки, что код работает правильно:
{`local score = 0
score = score + 10
print("Очки сейчас:", score) -- Очки сейчас: 10
local pos = game.Players.LocalPlayer.Character.HumanoidRootPart.Position
print("Игрок стоит в точке:", pos)`}}
/>
game.log по коду и посмотри, какие значения
печатаются. Так ты увидишь, где именно что-то пошло не так.
}
lua={
Если игра ведёт себя странно — расставь
print по коду и посмотри, какие значения
печатаются. Так ты увидишь, где именно что-то пошло не так.
Событие — это «что-то случилось». Скрипт может ждать событие и реагировать на него. Самые важные:
game.onTick(fn)game.onKey('space', fn)game.self.onClick(fn)game.self.onTouch(fn)RunService.Heartbeat:Connect(fn) | каждый кадр |
UserInputService.InputBegan:Connect(fn) | любая клавиша |
part.ClickDetector.MouseClick:Connect(fn) | клик по объекту |
part.Touched:Connect(fn) | касание объекта |
Пример — куб, который исчезает по клику:
{`local part = script.Parent
local clickDetector = Instance.new("ClickDetector")
clickDetector.Parent = part
clickDetector.MouseClick:Connect(function(player)
part:Destroy() -- удалить сам себя
print("Куб удалён!")
end)`}}
/>
{`() => { ... }`}? Это
«функция» — набор команд, упакованных вместе. Команды
внутри фигурных скобок выполнятся не сразу, а только
когда случится событие. То есть «когда кликнули — тогда
удалить и напечатать».
}
lua={
Что такое function() ... end? Это
«функция» — набор команд, упакованных вместе. Команды
между function() и end выполнятся
не сразу, а только когда случится событие. То есть
«когда кликнули — тогда удалить и напечатать».
Метод :Connect «подключает» функцию
к событию.
onTick выполняется ОЧЕНЬ часто — 60 раз
в секунду. Не делай внутри него тяжёлых вещей. Подробнее
об этой ошибке — раздел J4.
}
lua={Heartbeat выполняется ОЧЕНЬ часто — 60 раз
в секунду. Не делай внутри тяжёлых вычислений. Подробнее
об этой ошибке — раздел J4.
Условие — это развилка: «если что-то верно —
сделай одно, иначе — другое». В обоих языках это
слова if («если») и else
(«иначе»), но запись чуть отличается.
{`local coins = 7
if coins >= 10 then
print("Хватает на покупку!")
else
print("Нужно больше монет")
end`}}
/>
Тут проверяется: coins {'>'}= 10 — «монет
10 или больше?». Сейчас монет 7, значит условие неверно,
и сработает ветка else.
Знаки сравнения:
a === ba !== ba {'>'} ba {'<'} ba {'>'}= ba {'<'}= ba == b | a равно b |
a ~= b | a не равно b |
a {'>'} b | a больше b |
a {'<'} b | a меньше b |
a {'>'}= b | a больше или равно b |
a {'<'}= b | a меньше или равно b |
===, а не один. Один знак = —
это «положить значение в переменную», совсем другое
действие. И не равно — это !==.
}
lua={ ==. А «не равно» — это ~=
(тильда + равно). Запомни этот значок — он встречается
только в Lua.
Таймеры запускают команды не сразу, а потом:
game.after(сек, fn) — выполнить
один раз через несколько секунд;
game.every(сек, fn) — выполнять
снова и снова каждые несколько секунд;
game.cancel(id) — остановить таймер.
task.delay(сек, fn) — выполнить
один раз через несколько секунд;
task.wait(сек) — приостановить скрипт
на N секунд (внутри цикла или функции);
while true do task.wait(1); ... end
в отдельной корутине через task.spawn.
{`-- Через 3 секунды показать текст
task.delay(3, function()
print("Игра началась!")
end)
-- Каждую секунду прибавлять очко.
-- Запускаем в отдельной корутине чтобы не блокировать скрипт.
local score = 0
local running = true
task.spawn(function()
while running do
task.wait(1)
score = score + 1
end
end)
-- Через 10 секунд остановить начисление очков
task.delay(10, function()
running = false
print("Время вышло! Набрано очков:", score)
end)`}}
/>
(game.ui.score || 0) читается так:
«возьми счёт, а если его ещё нет — возьми 0». Это защита
от ошибки в самом начале, когда счётчик ещё пустой.
}
lua={
В Lua переменная running — флаг работы цикла.
Когда нужно остановить таймер, ставим running = false,
и цикл сам завершится после task.wait(1).
Это проще, чем хранить номер таймера.
Скриптом можно менять, как двигается игрок.
В JS используем команды-«множители»: 1 — обычно, 2 — в два раза сильнее, 0.5 — в два раза слабее.
game.player.setSpeed(mul) | скорость бега |
game.player.setJumpPower(mul) | сила прыжка |
game.player.setGravityMul(mul) | сила притяжения |
game.player.setDoubleJump(true) | двойной прыжок |
game.player.teleport(x,y,z) | мгновенно переставить |
В Lua скорость и прыжок — это прямые значения в Humanoid (не множители). По умолчанию WalkSpeed=16, JumpPower=50.
humanoid.WalkSpeed = 32 | скорость (16 = норма) |
humanoid.JumpPower = 80 | сила прыжка (50 = норма) |
workspace.Gravity = 100 | гравитация (196 = норма) |
humanoid:ChangeState(Enum.HumanoidStateType.Jumping) | прыгнуть |
hrp.CFrame = CFrame.new(x,y,z) | телепорт |
Пример — «зелье скорости» при касании сферы:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if not humanoid then return end
-- ускоряем игрока в 2 раза (16 → 32)
humanoid.WalkSpeed = 32
print("Скорость x2 на 5 секунд!")
-- зелье исчезает
part:Destroy()
-- через 5 секунд скорость обратно норма
task.delay(5, function()
humanoid.WalkSpeed = 16
end)
end)`}}
/>
Персонаж умеет показывать эмоции.
game.player.playAnimation(имя) проигрывает
анимацию: 'wave' (помахать),
'dance' (танец), 'cheer'
(радость), 'sit' (сесть).
}
lua={
В Lua анимации проигрываются через Animator на Humanoid'е.
Roblox-стиль: создать Animation-объект, вызвать
Animator:LoadAnimation(anim),
потом track:Play().
{`local Players = game:GetService("Players")
local player = Players.LocalPlayer
local humanoid = player.Character:WaitForChild("Humanoid")
local animator = humanoid:FindFirstChildOfClass("Animator")
-- Создаём анимацию (упрощённый шаблон — в реальности нужен AnimationId)
local anim = Instance.new("Animation")
-- anim.AnimationId = "rbxassetid://..." -- свой Animation ID
local track = animator:LoadAnimation(anim)
track:Play()
-- Через 3 секунды перестать
task.delay(3, function()
track:Stop()
end)`}}
/>
>
),
},
{
id: 'tweens',
title: 'E3. Твины — плавные движения',
body: (
<>
Твин — это плавное изменение чего-либо за время. Если просто переставить объект — он телепортируется рывком. А твин плавно доедет из точки в точку.
Команда: game.tween(объект, что менять, настройки)
{`// Находим платформу-лифт по имени
const lift = game.scene.findOne('Лифт');
// Платформа за 2 секунды плавно поднимается на высоту 10
game.tween(lift, { y: 10 }, {
duration: 2, // длительность в секундах
easing: 'ease' // характер движения
});`}
Полезные настройки твина:
duration | сколько секунд длится |
easing | 'linear' / 'ease' / 'bounce' |
repeat | сколько раз повторить |
yoyo: true | двигаться туда-обратно |
onDone | что сделать, когда твин закончится |
{`// Платформа вечно ездит вверх-вниз
const plat = game.scene.findOne('Качалка');
game.tween(plat, { y: 8 }, {
duration: 2,
yoyo: true,
repeat: 999
});`}
>}
lua={<>
В Lua используется TweenService — встроенный сервис Roblox-стиля. Создаёшь TweenInfo и Tween, вызываешь Play.
{`local TweenService = game:GetService("TweenService")
-- Находим платформу-лифт по имени
local lift = workspace:WaitForChild("Лифт")
-- Настройка анимации: 2 сек, плавно
local info = TweenInfo.new(2, Enum.EasingStyle.Quad)
-- Что менять: новая Position (поднимаем на 10 вверх)
local goal = { Position = lift.Position + Vector3.new(0, 10, 0) }
-- Создаём и запускаем твин
local tween = TweenService:Create(lift, info, goal)
tween:Play()`}
Полезные настройки TweenInfo:
| 1-й арг — секунды | сколько длится |
| EasingStyle | Linear / Quad / Bounce / Elastic |
| EasingDirection | In / Out / InOut |
| repeatCount | сколько раз повторить (-1 = бесконечно) |
| reverses | true = туда-обратно |
| tween.Completed:Connect | событие «закончен» |
{`-- Платформа вечно ездит вверх-вниз
local plat = workspace:WaitForChild("Качалка")
local info = TweenInfo.new(
2, -- секунды
Enum.EasingStyle.Quad, -- плавно
Enum.EasingDirection.InOut,
-1, -- бесконечно
true -- туда-обратно
)
local goal = { Position = plat.Position + Vector3.new(0, 8, 0) }
TweenService:Create(plat, info, goal):Play()`}
>}
/>
>
),
},
{
id: 'spawn-delete',
title: 'E4. Спавн и удаление объектов',
body: (
<>
Спавн — создание нового объекта прямо во время игры.
Команда game.scene.spawn(тип, настройки):
{`// Создаём золотую монетку-сферу
const coin = game.scene.spawn('primitive:sphere', {
x: 5, y: 1, z: 0, // где появится
color: '#ffd700' // золотой цвет
});
game.log('Создали монетку, её адрес:', coin);`}
Тип бывает 'block:трава',
'primitive:cube', 'model:tree'.
Команда возвращает ref — «адрес» объекта,
по которому к нему можно обращаться.
Удаление объекта:
{`game.scene.delete(coin); // сразу
game.scene.deleteAfter(coin, 3); // через 3 секунды`}
ref в переменную. Без адреса
ты потом не сможешь объект ни подвинуть, ни удалить.
Команда Instance.new("Part") создаёт новый Part:
{`-- Создаём золотую монетку-сферу
local coin = Instance.new("Part")
coin.Shape = Enum.PartType.Ball
coin.Size = Vector3.new(1, 1, 1)
coin.Position = Vector3.new(5, 1, 0)
coin.BrickColor = BrickColor.new("Bright yellow")
coin.Anchored = true
coin.Parent = workspace
print("Создали монетку:", coin)`}
Чтобы объект появился в игре — обязательно ставь
.Parent = workspace.
Anchored = true — чтобы не падал.
Удаление объекта:
{`coin:Destroy() -- сразу
-- через 3 секунды
game:GetService("Debris"):AddItem(coin, 3)`}
local coin = ...).
Без неё ты потом не сможешь объект ни подвинуть, ни удалить.
Передвинуть объект скриптом можно несколькими способами:
game.scene.move(ref,x,y,z)game.scene.rotate(ref,угол)game.self.move(x,y,z)game.tween(...)part.Position = Vector3.new(x,y,z) | мгновенно переставить |
part.CFrame = part.CFrame * CFrame.Angles(0, math.rad(45), 0) | повернуть |
script.Parent.Position = ... | скрипт двигает сам себя |
TweenService:Create(...) | плавное перемещение (E3) |
Пример — дверь уезжает вверх и освобождает проход:
{`local TweenService = game:GetService("TweenService")
local door = workspace:WaitForChild("Дверь")
-- плавно поднимаем дверь на 6 единиц вверх
local goal = { Position = door.Position + Vector3.new(0, 6, 0) }
TweenService:Create(door, TweenInfo.new(1), goal):Play()`}}
/>
>
),
},
],
},
// ════════════════════════════════════════════════════
// РАЗДЕЛ F — СКРИПТЫ — ИГРОВАЯ ЛОГИКА
// ════════════════════════════════════════════════════
{
id: 'scripts-logic',
icon: 'target',
title: 'Игровая логика',
summary: 'HP и урон, физика, теги, взаимодействие по E, связи между объектами.',
sections: [
{
id: 'player-hp',
title: 'F1. HP игрока: урон, лечение, смерть, чекпоинт',
body: (
<>
Команды для здоровья игрока:
game.player.hpgame.player.damage(n)game.player.heal(n)game.player.kill()game.player.respawn()game.player.setSpawn(точка)humanoid.Health | текущее здоровье |
humanoid:TakeDamage(n) | нанести урон |
humanoid.Health = humanoid.Health + n | вылечить |
humanoid.Health = 0 | мгновенно убить |
player:LoadCharacter() | воскресить |
player.RespawnLocation = spawn | новая точка возрождения |
Пример 1 — шипы наносят урон:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid:TakeDamage(20) -- отнять 20 здоровья
end
end)`}}
/>
Пример 2 — аптечка лечит:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if not humanoid then return end
-- добавить 50 здоровья, но не выше MaxHealth
humanoid.Health = math.min(humanoid.Health + 50, humanoid.MaxHealth)
print("+50 HP")
part:Destroy() -- аптечка исчезает
end)`}}
/>
>
),
},
{
id: 'physics',
title: 'F2. Физика: raycast, импульсы, взрывы',
body: (
<>
Отдел game.physics отвечает за «настоящую» физику:
raycast(откуда, куда, опции) — луч для стрельбы;applyImpulse(ref, сила) — толкнуть объект;explode(точка, радиус, опции) — взрыв.В Lua для физики используется workspace и стандартный Roblox API:
workspace:Raycast(origin, dir, params) — луч;part:ApplyImpulse(Vector3) — толкнуть Part;Instance.new("Explosion") — создать взрыв.Пример — стрельба лучом из камеры игрока:
{`local UIS = game:GetService("UserInputService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
UIS.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
local hrp = player.Character.HumanoidRootPart
local origin = hrp.Position + Vector3.new(0, 1.5, 0)
local direction = (mouse.Hit.Position - origin).Unit * 50
local raycastResult = workspace:Raycast(origin, direction)
if raycastResult then
print("Попал в объект:", raycastResult.Instance.Name)
end
end)`}}
/>
hit.hit — попал ли луч во что-нибудь.
hit.ref — адрес объекта.
}
lua={
raycastResult равно nil если
луч ни во что не попал. Иначе у него есть поля
.Instance (что попало),
.Position (точка попадания),
.Normal (нормаль поверхности).
Атрибут — это значение, которое ты «приклеиваешь» к объекту. Например, сколько здоровья у конкретного врага или сколько монет стоит товар.
{`local part = script.Parent
-- При старте игры запоминаем цену прямо на товаре
part:SetAttribute("Price", 50)
-- Когда игрок кликает — читаем цену
local clickDetector = Instance.new("ClickDetector", part)
clickDetector.MouseClick:Connect(function(player)
local price = part:GetAttribute("Price")
print("Этот товар стоит " .. price .. " монет")
end)`}}
/>
Чем атрибут лучше обычной переменной? Переменная одна на весь скрипт. А атрибут — свой у каждого объекта. Один и тот же скрипт можно повесить на 10 разных товаров, и у каждого будет своя цена.
> ), }, { id: 'tags', title: 'F4. Теги объектов', body: ( <>Тег — это «ярлык» на объекте. Удобно ставить сразу на много объектов и потом одной командой находить их все.
game.scene.tag(ref, 'звезда')game.scene.untag(ref, 'звезда')game.scene.hasTag(ref, 'звезда')game.scene.getTagged('звезда')CollectionService:AddTag(part, "звезда") | повесить тег |
CollectionService:RemoveTag(part, "звезда") | снять тег |
CollectionService:HasTag(part, "звезда") | есть ли тег |
CollectionService:GetTagged("звезда") | все объекты с тегом |
Пример — игра «собери все звёзды»:
{`local CS = game:GetService("CollectionService")
local part = script.Parent
-- Помечаем звезду тегом
CS:AddTag(part, "звезда")
part.Touched:Connect(function()
part:Destroy()
local left = #CS:GetTagged("звезда")
if left == 0 then
print("Все звёзды собраны! Победа!")
else
print("Осталось звёзд: " .. left)
end
end)`}}
/>
Часто игра просит «подойди и нажми E»: открыть сундук, поговорить с торговцем, дёрнуть рычаг.
{`local part = script.Parent
-- ProximityPrompt — стандартный Roblox-способ
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = "Открыть"
prompt.ObjectText = "Сундук"
prompt.MaxActivationDistance = 4
prompt.KeyboardKeyCode = Enum.KeyCode.E
prompt.Parent = part
prompt.Triggered:Connect(function(player)
print("Сундук открыт!")
-- Можно создать эффект частиц или проиграть звук
end)`}}
/>
Когда игрок подойдёт на расстояние взаимодействия, над объектом появится подсказка с текстом. Нажатие E запустит функцию.
> ), }, { id: 'billboard', title: 'F6. Billboard-метки над объектами', body: ( <>Billboard — это текст-табличка, которая висит над объектом в 3D-мире и всегда повёрнута к игроку. Так показывают имена врагов, их HP, названия мест.
{`-- BillboardGui в Roblox — это GUI поверх Part
local part = workspace:WaitForChild("NPC")
local billboard = Instance.new("BillboardGui")
billboard.Size = UDim2.new(4, 0, 1, 0)
billboard.StudsOffset = Vector3.new(0, 2.5, 0) -- над объектом
billboard.Parent = part
local label = Instance.new("TextLabel")
label.BackgroundTransparency = 1
label.Size = UDim2.new(1, 0, 1, 0)
label.Text = "Торговец Боб"
label.TextColor3 = Color3.new(1, 1, 1)
label.TextScaled = true
label.Parent = billboard
-- Позже можно убрать табличку
-- billboard:Destroy()`}}
/>
>
),
},
{
id: 'pass-through',
title: 'F7. Проходимость объектов',
body: (
<>
Иногда стена должна стать проходимой — призрачная стена, секретный проход, исчезающий мост.
{`local part = script.Parent
local clickDetector = Instance.new("ClickDetector", part)
clickDetector.MouseClick:Connect(function(player)
part.CanCollide = false -- игрок проходит насквозь
part.Transparency = 0.7 -- полупрозрачная (0=видна, 1=невидима)
print("Секретный проход открыт!")
end)`}}
/>
Связи (constraints) соединяют объекты, чтобы они двигались вместе или по правилам физики.
Пример — качели на петле:
{`-- В Roblox HingeConstraint — стандартный способ
local swing = workspace:WaitForChild("Качели")
local mount = workspace:WaitForChild("Опора") -- неподвижная точка
-- Attachment'ы (точки крепления)
local a0 = Instance.new("Attachment", mount)
local a1 = Instance.new("Attachment", swing)
local hinge = Instance.new("HingeConstraint")
hinge.Attachment0 = a0
hinge.Attachment1 = a1
hinge.ActuatorType = Enum.ActuatorType.Servo
hinge.ServoMaxTorque = 10000
hinge.AngularSpeed = 2
hinge.Parent = swing
-- Раскачиваем
local dir = 30
while true do
hinge.TargetAngle = dir
task.wait(1)
dir = -dir
end`}}
/>
>
),
},
],
},
// ════════════════════════════════════════════════════
// РАЗДЕЛ G — СКРИПТЫ — БОЛЬШИЕ СИСТЕМЫ
// ════════════════════════════════════════════════════
{
id: 'scripts-systems',
icon: 'gear',
title: 'Большие системы',
summary: 'NPC, инвентарь и оружие, звук, камера и катсцены, мультиплеер.',
sections: [
{
id: 'npc',
title: 'G1. NPC: создание, движение, диалоги',
body: (
<>
NPC (неигровой персонаж) — это житель твоей игры: торговец, враг, проводник.
В JS используем game.scene.spawnNpc(модель, опции):
{`// Создаём NPC по имени Боб
const bob = game.scene.spawnNpc('character-a', {
x: 5, y: 0, z: 0,
name: 'Боб',
hp: 100,
speed: 3
});
bob.say('Привет, путник!', 3); // реплика над головой
bob.moveTo(10, 0); // идёт в точку`}
Что умеет NPC:
moveTo(x, z) | идти в точку |
follow('player') | гнаться за игроком |
stop() | остановиться |
say(текст, сек) | реплика над головой |
damage(n) | нанести урон NPC |
remove() | убрать |
onDeath(fn) | при гибели |
Пример — враг гонится за игроком:
{`const enemy = game.scene.spawnNpc('character-b', {
x: 0, y: 0, z: 20, name: 'Враг', hp: 50, speed: 2
});
enemy.follow('player');
enemy.onDeath(() => {
game.ui.showText('Враг побеждён!', 2);
});`}
>}
lua={<>
В Lua NPC — это обычный Model с Humanoid внутри.
Движение делается через humanoid:MoveTo(point).
Реплики — через ChatService или BillboardGui.
{`-- NPC модель должна лежать в Workspace.
-- Внутри Model должны быть Part'ы и Humanoid.
local npc = workspace:WaitForChild("Боб")
local humanoid = npc:WaitForChild("Humanoid")
local hrp = npc:WaitForChild("HumanoidRootPart")
-- Реплика над головой через BillboardGui
local function say(text, duration)
local bg = Instance.new("BillboardGui")
bg.Size = UDim2.new(4, 0, 1, 0)
bg.StudsOffset = Vector3.new(0, 3, 0)
bg.Parent = npc.Head or hrp
local label = Instance.new("TextLabel", bg)
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.TextColor3 = Color3.new(1, 1, 1)
label.TextScaled = true
label.Text = text
task.delay(duration, function() bg:Destroy() end)
end
say("Привет, путник!", 3)
-- Идёт в точку
humanoid:MoveTo(Vector3.new(10, hrp.Position.Y, 0))`}
Враг гонится за игроком:
{`local enemy = workspace:WaitForChild("Враг")
local humanoid = enemy.Humanoid
local Players = game:GetService("Players")
-- Каждые 0.5 сек обновляем цель — позицию игрока
task.spawn(function()
while enemy.Parent do
local player = Players:GetPlayers()[1]
if player and player.Character then
local target = player.Character.HumanoidRootPart.Position
humanoid:MoveTo(target)
end
task.wait(0.5)
end
end)
-- При гибели
humanoid.Died:Connect(function()
print("Враг побеждён!")
end)`}
>}
/>
>
),
},
{
id: 'inventory-tools',
title: 'G2. Инвентарь и инструменты',
body: (
<>
Инвентарь — сумка предметов. Инструмент — предмет, который игрок берёт в руку: меч, фонарик.
{`// Выдать игроку меч прямо в руку
game.player.giveTool('sword', {
name: 'Стальной меч',
equip: true
});
// Ловим, когда игрок применил инструмент (ЛКМ)
game.player.onToolUse((e) => {
game.log('Игрок применил:', e.tool);
});`}
Команды game.inventory: add,
remove, has, list.
{`-- В Roblox инструмент — это Tool-инстанс в Backpack игрока.
local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- Создаём меч
local sword = Instance.new("Tool")
sword.Name = "Стальной меч"
sword.RequiresHandle = false -- упрощённо без Handle-Part
sword.Parent = player.Backpack
-- Сразу взять в руку (переложить в Character)
sword.Parent = player.Character
-- Ловим применение (ЛКМ или активация)
sword.Activated:Connect(function()
print("Игрок применил меч!")
end)`}
Инвентарь игрока = его Backpack (Roblox-сервис).
Чтобы посмотреть что есть: player.Backpack:GetChildren().
Пример — игра «ключ и сундук»:
{`local part = script.Parent
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = "Открыть"
prompt.MaxActivationDistance = 4
prompt.Parent = part
prompt.Triggered:Connect(function(player)
-- Ищем ключ в Backpack
local key = player.Backpack:FindFirstChild("Ключ")
if not key then
key = player.Character and player.Character:FindFirstChild("Ключ")
end
if key then
print("Сундук открыт!")
key:Destroy() -- ключ потрачен
else
print("Нужен ключ")
end
end)`}}
/>
>
),
},
{
id: 'sound',
title: 'G3. Звук: свои звуки и 3D-позиционный звук',
body: (
<>
Звук оживляет игру.
В JS — команда game.sound.play(id, опции):
{`// Готовые звуки-пресеты
game.sound.play('coin');
game.sound.play('win');
game.sound.play('jump');
game.sound.play('hit');
// Свой загруженный звук, потише
game.sound.play('sound_1', { volume: 0.7 });`}
Пресеты: jump, pickup,
win, lose, click,
hit, coin.
3D-звук — опция at привязывает
звук к точке в мире, тише с расстоянием.
{`game.sound.play('sound_2', {
at: { x: 0, y: 1, z: 0 },
loop: true
});`}
>}
lua={<>
В Lua используется Sound-инстанс:
{`-- Простой звук — играет везде одинаково
local sound = Instance.new("Sound")
sound.SoundId = "rbxassetid://9120386436" -- свой ID
sound.Volume = 0.7
sound.Parent = workspace
sound:Play()`}
3D-звук — родителем ставим Part в мире. Sound автоматически становится позиционным.
{`-- Звук костра — слышен близко
local campfire = workspace.Костёр
local sound = Instance.new("Sound")
sound.SoundId = "rbxassetid://..."
sound.RollOffMaxDistance = 30 -- метры до полной тишины
sound.Looped = true
sound.Parent = campfire -- родитель = Part → 3D-звук
sound:Play()`}
>}
/>
В JS — отдел game.camera:
setFov(градусы) | угол обзора |
shake(сила, сек) | тряска камеры |
focusOn(ref) | навести на объект |
cutscene(точки, опции) | пролёт камеры |
reset() | вернуть игроку |
{`// Облёт уровня при старте
game.camera.cutscene([
{ x: 0, y: 20, z: -30 },
{ x: 0, y: 15, z: 0 },
{ x: 0, y: 10, z: 30 }
], { segDuration: 2 });
game.onCutsceneDone(() => {
game.ui.showText('Поехали!', 2);
});`}
>}
lua={<>
В Lua — стандартный Roblox Camera через Workspace.CurrentCamera:
camera.FieldOfView = 90 | угол обзора |
camera.CameraType = Enum.CameraType.Scriptable | отключить авто-следование |
camera.CFrame = CFrame.new(pos, look) | поставить камеру |
TweenService:Create(camera, ...) | плавный пролёт |
{`local TweenService = game:GetService("TweenService")
local camera = workspace.CurrentCamera
-- Отключаем авто-следование за игроком
camera.CameraType = Enum.CameraType.Scriptable
-- Облёт через 3 точки за 6 секунд (3 этапа по 2 сек)
local points = {
Vector3.new(0, 20, -30),
Vector3.new(0, 15, 0),
Vector3.new(0, 10, 30),
}
for _, point in ipairs(points) do
local goal = { CFrame = CFrame.new(point, Vector3.new(0, 5, 0)) }
local tween = TweenService:Create(camera, TweenInfo.new(2), goal)
tween:Play()
tween.Completed:Wait()
end
-- Вернуть камеру игроку
camera.CameraType = Enum.CameraType.Custom
print("Поехали!")`}
>}
/>
>
),
},
{
id: 'beam-trail',
title: 'G5. Лучи и следы (Beam и Trail)',
body: (
<>
Beam — светящаяся линия между двумя точками (лазер, мост света), Trail — шлейф за движущимся объектом (след за ракетой).
{`-- В Roblox Beam — это инстанс на Attachment'е
local t1 = workspace:WaitForChild("Башня1")
local t2 = workspace:WaitForChild("Башня2")
-- Attachment'ы — точки на Part'ах
local a0 = Instance.new("Attachment", t1)
local a1 = Instance.new("Attachment", t2)
local beam = Instance.new("Beam")
beam.Attachment0 = a0
beam.Attachment1 = a1
beam.Color = ColorSequence.new(Color3.fromRGB(255, 51, 68))
beam.Width0 = 0.3
beam.Width1 = 0.3
beam.LightEmission = 1
beam.Parent = t1`}}
/>
>
),
},
{
id: 'multiplayer',
title: 'G6. Мультиплеер: игроки, комната, команды',
body: (
<>
В Рублоксе можно сделать игру на несколько игроков.
В JS — отделы game.players, game.room, game.teams:
{`// Общий счёт команды — виден всем игрокам
game.room.set('totalScore', 0);
// когда счёт меняется — обновляем надпись
game.room.onChange('totalScore', (val) => {
game.ui.set('score', 'Счёт команды: ' + val);
});
game.log('Игроков:', game.players.count());
game.onPlayerJoin((p) => {
game.ui.showText(p.name + ' присоединился!', 2);
});`}
>}
lua={<>
В Lua — стандартный Roblox-стиль:
{`local Players = game:GetService("Players")
local Teams = game:GetService("Teams")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Общий счёт команды через NumberValue в ReplicatedStorage
-- (виден всем игрокам через .Changed)
local totalScore = Instance.new("NumberValue")
totalScore.Name = "TotalScore"
totalScore.Value = 0
totalScore.Parent = ReplicatedStorage
totalScore.Changed:Connect(function(newValue)
print("Счёт команды:", newValue)
end)
print("Игроков:", #Players:GetPlayers())
-- Когда новый игрок зашёл
Players.PlayerAdded:Connect(function(player)
print(player.Name .. " присоединился!")
end)`}
Команды (Teams):
{`-- Команды создают в Teams сервисе или скриптом
local redTeam = Instance.new("Team")
redTeam.Name = "Red"
redTeam.TeamColor = BrickColor.new("Bright red")
redTeam.AutoAssignable = true
redTeam.Parent = game:GetService("Teams")
-- Назначить игрока в команду
Players.PlayerAdded:Connect(function(player)
player.Team = redTeam
end)`}
>}
/>
>
),
},
{
id: 'leaderstats',
title: 'G7. Лидерборды и достижения',
body: (
<>
Лидерборд — таблица очков справа-сверху (как в Roblox).
{`game.leaderstats.define('Монеты', { initial: 0, icon: 'coin' });
game.leaderstats.define('Уровень', { initial: 1 });
game.leaderstats.me.add('Монеты', 5);
game.leaderstats.me.set('Уровень', 2);
const c = game.leaderstats.me.get('Монеты');`}
Достижения:
{`game.achievements.define([
{ id: 'first_coin', name: 'Первая монетка', icon: 'coin', rarity: 'common' },
{ id: 'rich', name: 'Богач', description: '100 монет', icon: 'trophy', rarity: 'legendary' }
]);
game.achievements.unlock('first_coin');
game.achievements.bindToStat('rich', 'Монеты', 100);`}
>}
lua={<>
В Lua — стандартный Roblox-паттерн: создаём папку
leaderstats в Player с IntValue внутри:
{`local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
-- Папка leaderstats — Roblox автоматически показывает её в HUD
local stats = Instance.new("Folder")
stats.Name = "leaderstats"
stats.Parent = player
-- Стат "Монеты"
local coins = Instance.new("IntValue")
coins.Name = "Монеты"
coins.Value = 0
coins.Parent = stats
-- Стат "Уровень"
local level = Instance.new("IntValue")
level.Name = "Уровень"
level.Value = 1
level.Parent = stats
end)
-- Добавить монеты текущему игроку
local function addCoins(player, amount)
local stats = player:FindFirstChild("leaderstats")
if stats then
stats.Монеты.Value = stats.Монеты.Value + amount
end
end`}
Папка с именем leaderstats на игроке —
магическое имя в Roblox. Любые IntValue/NumberValue/StringValue
внутри неё автоматически попадают в HUD справа сверху.
Всплывающие цифры урона над врагом — как в RPG.
В JS это готовая команда:
{`game.fx.autoMobFloaters(true); // авто для всех мобов
// или вручную
game.fx.damageFloater(enemy.position, 25);
game.fx.damageFloater(enemy.position, 100, { isCrit: true });
game.fx.damageFloater('player', 30, { isHeal: true });
game.fx.damageFloater(pos, 0, { isMiss: true });`}
>}
lua={<>
В Lua делаем сами через BillboardGui + TweenService:
{`local TweenService = game:GetService("TweenService")
local function showDamage(position, amount, isCrit)
-- Невидимый Part-якорь в нужной точке
local anchor = Instance.new("Part")
anchor.Anchored = true
anchor.CanCollide = false
anchor.Transparency = 1
anchor.Size = Vector3.new(0.1, 0.1, 0.1)
anchor.Position = position + Vector3.new(0, 2, 0)
anchor.Parent = workspace
-- BillboardGui над якорем
local bg = Instance.new("BillboardGui", anchor)
bg.Size = UDim2.new(3, 0, 1, 0)
bg.AlwaysOnTop = true
local label = Instance.new("TextLabel", bg)
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.Text = "-" .. amount
label.TextColor3 = isCrit
and Color3.fromRGB(255, 200, 0) -- жёлтый крит
or Color3.fromRGB(255, 80, 80) -- красный обычный
label.TextScaled = true
label.Font = Enum.Font.GothamBold
-- Анимируем вверх + исчезание
local goal = { Position = anchor.Position + Vector3.new(0, 4, 0) }
TweenService:Create(anchor, TweenInfo.new(1), goal):Play()
task.delay(1, function() anchor:Destroy() end)
end
-- Пример использования:
showDamage(workspace.Враг.Position, 25, false)
showDamage(workspace.Враг.Position, 100, true) -- крит`}
>}
/>
>
),
},
{
id: 'items-inventory',
title: 'G9. Предметы и инвентарь с редкостями',
body: (
<>
В JS — готовый отдел game.items и game.inventory:
{`game.items.define([
{ id: 'berry', name: 'Ягоды', emoji: '🍓', rarity: 'common', maxStack: 16 },
{ id: 'potion', name: 'Зелье', emoji: '🧪', rarity: 'rare', maxStack: 8, onUseEffect: 'heal:50' },
{ id: 'sword', name: 'Меч', emoji: '⚔️', rarity: 'legendary', maxStack: 1 },
]);
game.inventory.give('sword', 1);
game.inventory.give('berry', 5);`}
Сбор предмета с земли:
{`game.self.onInteract(() => {
game.inventory.give('berry', 2);
game.self.delete();
}, { text: 'Собрать', distance: 3 });`}
В Roblox инвентарь — это Backpack игрока с Tool'ами,
плюс свои IntValue'ы для подсчёта стаков.
Готового «инвентаря с редкостями» нет — собирается из частей:
{`-- Пример: ягоды как IntValue в leaderstats
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
local stats = Instance.new("Folder")
stats.Name = "leaderstats"
stats.Parent = player
local berries = Instance.new("IntValue", stats)
berries.Name = "Ягоды"
berries.Value = 0
end)
-- Сбор ягод (скрипт на собираемом Part)
local part = script.Parent
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = "Собрать"
prompt.Parent = part
prompt.Triggered:Connect(function(player)
local berries = player.leaderstats and player.leaderstats:FindFirstChild("Ягоды")
if berries then
berries.Value = berries.Value + 2
part:Destroy()
end
end)`}
Для полноценной системы с редкостями, иконками и UI окном —
надо собирать ScreenGui вручную (по статье C). Это много кода —
проще использовать JS-вариант с готовым game.items.
Пресеты неба одной командой:
{`game.scene.setSkybox({ preset: 'sunset' });
// пресеты: clear-summer-day | lowpoly-roblox | cloudy | sunset | starry-night | space
game.scene.setClouds({ enabled: true, cover: 0.5, speed: 0.02 });
game.scene.setFog({ color: '#dddddd', density: 0.006 });
game.scene.skybox.fadeTo({ preset: 'starry-night' }, 3);
game.environment.setSkyColor('#0a1024');
game.environment.setTimeOfDay(0); // ночь
game.environment.setTimeOfDay(12); // полдень`}
>}
lua={<>
В Roblox небо — это инстансы Sky и Atmosphere
в Lighting:
{`local Lighting = game:GetService("Lighting")
-- Sky-инстанс с собственными текстурами
local sky = Instance.new("Sky")
sky.SkyboxBk = "rbxassetid://..." -- задняя грань
sky.SkyboxFt = "rbxassetid://..." -- передняя
sky.SkyboxLf = "rbxassetid://..." -- левая
sky.SkyboxRt = "rbxassetid://..." -- правая
sky.SkyboxUp = "rbxassetid://..." -- верх
sky.SkyboxDn = "rbxassetid://..." -- низ
sky.Parent = Lighting
-- Туман
Lighting.FogColor = Color3.fromRGB(221, 221, 221)
Lighting.FogStart = 50
Lighting.FogEnd = 500
-- Atmosphere (мгла, плотность)
local atmosphere = Instance.new("Atmosphere")
atmosphere.Density = 0.3
atmosphere.Color = Color3.fromRGB(199, 199, 199)
atmosphere.Parent = Lighting
-- Время суток (часы и минуты от полуночи)
Lighting:SetMinutesAfterMidnight(12 * 60) -- полдень
Lighting:SetMinutesAfterMidnight(0) -- полночь`}
>}
/>
>
),
},
{
id: 'modal-menu-loading',
title: 'G11. Диалоги, меню, экран загрузки',
body: (
<>
Диалог NPC:
{`game.modal.dialog('Староста', [
'Привет, путник!',
'Собери 10 монет и возвращайся.',
], () => game.ui.showText('Квест начат!', 2));`}
Окно Да/Нет и лутбокс:
{`game.modal.confirmation('Выход', 'Точно выйти?',
() => game.player.respawn(), null);
game.modal.lootbox([
{ name: 'Меч', color: '#f0ad4e', rarity: 'legendary' },
{ name: 'Щит', color: '#5bc0de', rarity: 'rare' },
], (item) => game.ui.showText('Выпал: ' + item.name, 3));`}
Экран загрузки:
{`game.loading.show({
style: 'ken-burns',
placeName: 'Глава 2 — Шахта',
duration: 2
});
game.loading.onHide(() => game.ui.showText('Добро пожаловать!', 2));`}
>}
lua={<>
В Roblox/Lua нет готовых модалок — всё собирается через ScreenGui с Frame'ами. Это много кода (~30-100 строк на диалог), но полностью кастомизируется.
{`-- Простейший диалог: ScreenGui + Frame + TextLabel + Button
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local gui = player:WaitForChild("PlayerGui")
local function showDialog(speaker, lines, onDone)
local screen = Instance.new("ScreenGui", gui)
local frame = Instance.new("Frame", screen)
frame.Size = UDim2.new(0.6, 0, 0.25, 0)
frame.Position = UDim2.new(0.2, 0, 0.65, 0)
frame.BackgroundColor3 = Color3.new(0, 0, 0)
frame.BackgroundTransparency = 0.4
local nameLabel = Instance.new("TextLabel", frame)
nameLabel.Size = UDim2.new(1, 0, 0.2, 0)
nameLabel.Text = speaker
nameLabel.TextColor3 = Color3.fromRGB(255, 220, 100)
nameLabel.BackgroundTransparency = 1
local textLabel = Instance.new("TextLabel", frame)
textLabel.Size = UDim2.new(1, -20, 0.6, 0)
textLabel.Position = UDim2.new(0, 10, 0.2, 0)
textLabel.TextColor3 = Color3.new(1, 1, 1)
textLabel.BackgroundTransparency = 1
textLabel.TextWrapped = true
local idx = 1
local function showLine()
textLabel.Text = lines[idx]
end
local btn = Instance.new("TextButton", frame)
btn.Size = UDim2.new(0.3, 0, 0.15, 0)
btn.Position = UDim2.new(0.65, 0, 0.82, 0)
btn.Text = "Дальше"
btn.MouseButton1Click:Connect(function()
idx = idx + 1
if idx > #lines then
screen:Destroy()
if onDone then onDone() end
else
showLine()
end
end)
showLine()
end
showDialog("Староста", {
"Привет, путник!",
"Собери 10 монет и возвращайся.",
}, function() print("Квест начат!") end)`}
>}
/>
>
),
},
{
id: 'vehicles-menu',
title: 'G12. Машины и главное меню',
body: (
<>
Машина, на которой можно ездить (вход hold-F, WASD руль):
{`game.scene.spawn('vehicle:car', { x: 0, y: 1, z: 0, name: 'Тачка' });
game.onVehicleEnter(() => game.ui.showText('За рулём! WASD — ехать', 2));
game.onVehicleExit(() => game.ui.showText('Вышел', 1));`}
Главное меню:
{`game.mainMenu.show({
title: 'МОЯ ИГРА',
camera: 'orbit',
playButtonText: 'ИГРАТЬ',
patchNotes: { title: 'Что нового', items: ['Добавлены машины', 'Новая карта'] },
onPlay: () => game.ui.showText('Поехали!', 2)
});`}
>}
lua={<>
В Roblox машина — это сложная Model с VehicleSeat
внутри. Когда игрок садится в VehicleSeat — у него
появляются .Throttle и .Steer
свойства от WASD автоматически:
{`-- VehicleSeat внутри Model
local seat = workspace:WaitForChild("Тачка"):WaitForChild("VehicleSeat")
-- Слушаем игрока в кресле
seat:GetPropertyChangedSignal("Occupant"):Connect(function()
if seat.Occupant then
print("За рулём! WASD — ехать")
else
print("Вышел")
end
end)
-- Throttle (W/S) и Steer (A/D) — автоматически в seat.Throttle и seat.Steer
-- Применяй их к скорости/повороту в RunService.Heartbeat`}
Главное меню — собирается через ScreenGui (см. C2-C3), или используется готовый StarterGui от Roblox.
>} /> > ), }, ], }, // ════════════════════════════════════════════════════ // РАЗДЕЛ H — СПРАВОЧНИК game.* // ════════════════════════════════════════════════════ { id: 'reference', icon: 'book', title: 'Справочник game.*', summary: 'Шпаргалка: все команды game.* списком по отделам.', sections: [ { id: 'cheatsheet', title: 'H1. Шпаргалка — все команды списком', body: ( <>Здесь собраны все команды по отделам. Это шпаргалка — не нужно её запоминать, держи под рукой. Переключатель сверху меняет язык.
game.player.positiongame.player.hp / maxHpgame.player.alivegame.player.forwardgame.player.teleport(x,y,z)game.player.damage(n) / heal(n)game.player.kill() / respawn()game.player.setSpawn(точка)game.player.setSpeed(mul)game.player.setJumpPower(mul)game.player.setGravityMul(mul)game.player.setDoubleJump(on)game.player.playAnimation(имя)game.player.giveTool(тип,опции)game.player.isKeyDown(клавиша)hrp.Position | позиция (Vector3) |
humanoid.Health / MaxHealth | здоровье |
humanoid.Health {'>'} 0 | жив ли |
camera.CFrame.LookVector | куда смотрит |
hrp.CFrame = CFrame.new(x,y,z) | телепорт |
humanoid:TakeDamage(n) / humanoid.Health += n | урон / лечение |
humanoid.Health = 0 / player:LoadCharacter() | убить / воскресить |
player.RespawnLocation = spawn | точка возрождения |
humanoid.WalkSpeed = N | скорость (16 = норма) |
humanoid.JumpPower = N | сила прыжка (50 = норма) |
workspace.Gravity = N | гравитация (196 = норма) |
humanoid:ChangeState(Jumping) | прыгнуть |
animator:LoadAnimation(anim):Play() | анимация |
Instance.new("Tool",player.Character) | инструмент в руку |
UserInputService:IsKeyDown(key) | зажата ли клавиша |
game.scene.spawn(тип,опции)game.scene.delete(ref)game.scene.deleteAfter(ref,сек)game.scene.move(ref,x,y,z)game.scene.rotate(ref,угол)game.scene.setColor(ref,цвет)game.scene.setCollide(ref,да)game.scene.setVisible(ref,да)game.scene.setOpacity(ref,0..1)game.scene.find(имя) / findOne(имя)game.scene.all(тип)game.scene.setData/getDatagame.scene.tag/untag/hasTaggame.scene.getTagged(тег)game.scene.setLabel/clearLabelgame.scene.spawnNpc(модель,опции)game.scene.spawnParticles(тип,...)Instance.new("Part", workspace) | создать объект |
part:Destroy() | удалить |
Debris:AddItem(part, N) | удалить через N секунд |
part.Position = Vector3.new(x,y,z) | переместить |
part.Orientation = Vector3.new(...) | повернуть |
part.Color = Color3.fromRGB(...) | цвет |
part.CanCollide = true/false | твёрдость |
part.Transparency = 1 | невидимость (0=видно) |
part.Transparency = 0.4 | полупрозрачность |
workspace:FindFirstChild("Имя") / workspace.Имя | поиск по имени |
CollectionService:GetTagged("тег") | все объекты с тегом |
part:SetAttribute/GetAttribute | атрибуты |
CollectionService:AddTag/RemoveTag/HasTag | теги |
CollectionService:GetTagged(tag) | все объекты с тегом |
BillboardGui + TextLabel | метка над объектом |
| Model + Humanoid + Anim | NPC (вручную) |
Instance.new("ParticleEmitter", part) | частицы |
game.self.ref / positiongame.self.onClick(fn)game.self.onTouch(fn)game.self.onUntouch(fn)game.self.onInteract(fn,опции)game.self.move(x,y,z)game.self.delete()game.self.setText(t)script.Parent / .Position | сам объект и его позиция |
ClickDetector.MouseClick:Connect | клик по объекту |
part.Touched:Connect | игрок коснулся |
part.TouchEnded:Connect | игрок вышел |
ProximityPrompt.Triggered:Connect | взаимодействие по E |
part.Position = Vector3.new(x,y,z) | переместить |
part:Destroy() | удалить |
textLabel.Text = "..." | сменить текст (для GUI) |
game.ui.score / timergame.ui.showText(текст,сек)game.ui.set(id,текст,опции)game.ui.remove(id) / clear()leaderstats папка + IntValue | счётчики в углу (HUD автомат) |
ScreenGui + TextLabel (центр) | текст по центру |
label.Text = "..." | обновить метку |
label:Destroy() / screen:Destroy() | убрать метку / всё |
game.gui.find(имя) / get(id)game.gui.update(id,patch)game.gui.show(id) / hide(id)game.gui.onClick(id,fn)game.gui.onSubmit(id,fn)gui:FindFirstChild(имя, true) | найти элемент |
elem.Text = "..." / прямая запись свойств | изменить свойства |
elem.Visible = true/false | показать / скрыть |
button.MouseButton1Click:Connect | клик по кнопке |
textbox.FocusLost:Connect(fn) | ввод завершён |
game.physics.raycast(...)game.physics.applyImpulse(...)game.physics.explode(...)game.physics.passThrough(...)game.fx.beam(опции)game.fx.trail(ref,опции)game.fx.damageFloater(...)game.constraints.weld(a,b)game.constraints.hinge(...)game.constraints.spring(...)workspace:Raycast(origin,dir,params) | луч — во что попал |
part:ApplyImpulse(Vector3) | толкнуть объект |
Instance.new("Explosion", workspace) | взрыв |
part.CanCollide = false | проходимость |
Instance.new("Beam") + Attachments | светящийся луч |
Instance.new("Trail") + Attachments | след за объектом |
| BillboardGui + TweenService | цифры урона (вручную) |
Instance.new("WeldConstraint") | склейка |
Instance.new("HingeConstraint") | петля |
Instance.new("SpringConstraint") | пружина |
game.camera.setFov(град)game.camera.shake(сила,сек)game.camera.cutscene(...)game.camera.reset()game.sound.play(id,опции)workspace.CurrentCamera.FieldOfView = N | угол обзора |
camera.CFrame = CFrame.new(...) + рандом | тряска (вручную) |
camera.CameraType = Scriptable + TweenService | пролёт камеры |
camera.CameraType = Custom | вернуть игроку |
Instance.new("Sound"):Play() | проиграть звук |
game.onTick(fn)game.onKey/onKeyUp(клавиша,fn)game.onClick(fn)game.after(сек,fn)game.every(сек,fn)game.cancel(id)game.tween(ref,св-ва,опции)RunService.Heartbeat:Connect(fn) | каждый кадр |
UserInputService.InputBegan/Ended | клавиатура |
mouse.Button1Down:Connect(fn) | клик в игре |
task.delay(сек, fn) | через N секунд |
task.spawn(function() while ... task.wait(N) end end) | каждые N секунд |
connection:Disconnect() | отменить подписку |
TweenService:Create(obj, info, goal):Play() | плавная анимация |
game.random(min,max)game.distance(a,b)game.clamp(v,min,max)game.lerp(a,b,t)game.log(...)game.broadcast/onMessagemath.random(min,max) | случайное число |
(a - b).Magnitude | расстояние между Vector3 |
math.clamp(v,min,max) | зажать в границах |
a + (b-a)*t или Vector3:Lerp(other,t) | плавный переход |
print(...) / warn(...) | в консоль |
BindableEvent:Fire + .Event:Connect | сообщения между скриптами |
game.players.all() / count() / me()game.room.set/get/onChangegame.teams.*game.leaderstats.define(имя,опции)game.leaderstats.me.add/set/getgame.achievements.define/unlockgame.save.merge/getgame.onPlayerJoin/Leave(fn)Players:GetPlayers() / #Players:GetPlayers() / Players.LocalPlayer | список / число / я |
ReplicatedStorage + Value + .Changed | общее состояние |
Teams сервис + Instance.new("Team") | команды |
Instance.new("Folder","leaderstats")+IntValue | лидерборд |
stats.Имя.Value = N | обновить стат |
BadgeService:AwardBadge(uid, id) | достижения (badges) |
DataStoreService:GetAsync/SetAsync | сохранение прогресса |
Players.PlayerAdded:Connect / PlayerRemoving | игрок зашёл / ушёл |
game.scene.setSkybox/fadeTogame.scene.setFog/setCloudsgame.environment.setTimeOfDay(0..24)game.items.define(список)game.inventory.give/remove/has/listgame.modal.dialog/confirmation/lootboxgame.mainMenu.show/hidegame.loading.show/onHideLighting + Sky / Atmosphere | пресеты неба (вручную) |
Lighting.FogColor / FogEnd / Atmosphere | туман и облака |
Lighting:SetMinutesAfterMidnight(N) | время суток |
| Свои Tool'ы в ServerStorage | предметы (вручную) |
player.Backpack:GetChildren() / Tool.Parent = Backpack | инвентарь |
| ScreenGui + Frame + Button | модалки (вручную, см. G11) |
| ScreenGui + Frame | главное меню (вручную) |
ReplicatedFirst + loading screen | экран загрузки |
Словарик слов, которые встречаются в вики:
| Примитив | Простая 3D-фигура: куб, сфера, цилиндр. Главный строительный материал. |
| Модель | Готовая красивая 3D-фигура из библиотеки (дерево, машина). |
| Блок | Кубик одного размера, ровно встаёт по сетке. |
| Сцена | Весь игровой мир — всё, что ты построил. |
| Вьюпорт | Окно с 3D-сценой в центре редактора. |
| Гизмо | Цветные стрелки и кольца для перемещения объектов. |
| Иерархия | Список всех объектов игры в правой панели. |
| Инспектор | Панель со свойствами выделенного объекта. |
| Скрипт | Набор команд (код), который оживляет игру. |
| JavaScript | Язык программирования, на котором пишут скрипты. |
| Глобальный скрипт | Скрипт-«мозг», не привязан к объекту, запускается один раз. |
| Скрипт на объекте | Скрипт конкретного объекта, в нём работает game.self. |
| Переменная | «Коробочка с именем» — скрипт хранит в ней значение. |
| Функция | Набор команд, который выполнится, когда его позовут. |
| Событие | «Что-то случилось» — клик, касание, нажатие клавиши. |
| Условие (if) | Развилка: если что-то верно — сделай одно, иначе — другое. |
| Спавн | Появление: точка спавна — где появляется игрок; «заспавнить» — создать объект. |
| Респаун | Воскрешение игрока после смерти. |
| Чекпоинт | Контрольная точка — место, откуда игрок воскреснет. |
| HP | Здоровье игрока. При HP=0 он умирает. |
| ref | «Адрес» объекта. Команда spawn возвращает ref, по нему обращаются к объекту. |
| Твин | Плавное изменение свойства за время (плавное движение). |
| Тег | Ярлык на объекте. По тегу можно найти все помеченные объекты. |
| Атрибут | Своё значение, приклеенное к объекту (через setData). |
| Констрейнт | Связь между объектами: склейка, петля, пружина. |
| Эмиттер | Объект, создающий частицы (искры, дым, огонь). |
| Частицы | Много маленьких летящих точек для эффектов. |
| GUI | Интерфейс: кнопки, надписи, меню поверх 3D-сцены. |
| HUD | Счётчики и индикаторы поверх экрана (счёт, HP). |
| NPC | Неигровой персонаж: торговец, враг, проводник. |
| Триггер | Невидимая зона, которая что-то запускает при входе игрока. |
| Raycast | Невидимый луч — узнать, во что он попал (для стрельбы). |
| FOV | Угол обзора камеры. Больше — «шире» видно. |
| Катсцена | Видеовставка: камера сама пролетает по точкам. |
| Мультиплеер | Игра на нескольких игроков в одной комнате. |
| Модерация | Проверка игры перед публикацией в общей ленте. |
Первым делом — проверь Консоль. Если в скрипте опечатка, её текст появится в Консоли красным — там написано, в какой строке ошибка.
Самые частые опечатки:
; в конце команды; ( ), {`{ }`} должно быть
поровну;
game.player.teelport
вместо teleport;
с и английская c).
После правки скрипта — сохрани игру (Ctrl+S) и перезапусти Play.
> ), }, { id: 'fell-through-floor', title: 'J2. Объект провалился сквозь пол', body: (Если объект не закреплён (свойство «Закреплён» выключено), он падает под действием физики. Для платформ, стен и декораций включи «Закреплён» в инспекторе — тогда объект будет висеть на месте. Падать должны только те объекты, которым это нужно (ящики, мячи).
), }, { id: 'findone-null', title: 'J3. findOne вернул «ничего» (null) на старте', body: ( <>
Если позвать game.scene.findOne(...) в самой
первой строке скрипта — объект может быть ещё не готов,
и команда вернёт null («ничего»). Потом
обращение к этому null сломает скрипт.
Решение: ищи объект не на старте, а внутри
onTick или после небольшой задержки:
{`let door = null;
game.onTick(() => {
// ищем дверь, пока не найдём
if (!door) door = game.scene.findOne('Дверь');
// ... работаем с door, когда он уже найден
});`}
>
),
},
{
id: 'ui-set-every-frame',
title: 'J4. game.ui.set каждый кадр — игра лагает',
body: (
<>
onTick выполняется 60 раз в секунду. Если
внутри него на каждом кадре звать game.ui.set(...),
интерфейс будет обновляться слишком часто и игра начнёт
тормозить.
Решение: обновляй интерфейс только когда значение реально изменилось:
{`let lastScore = -1;
game.onTick(() => {
const s = game.ui.score || 0;
if (s !== lastScore) { // значение изменилось?
game.ui.set('hud', 'Счёт: ' + s);
lastScore = s;
}
});`}
>
),
},
{
id: 'other-mistakes',
title: 'J5. Прочие типичные грабли',
body: (
volume) и что звук загружен. Не запускай
длинный звук в самом начале — это тормозит старт.
game.gui.onClick(...) с правильным id,
и что у элемента правильное имя.
{`{ y: 10 }`}), а первым
аргументом — настоящий ref объекта.
passThrough
аккуратно.
В Рублоксе есть два вида скриптов:
game.scene, game.player, game.ui.game.self — это сам объект-носитель.
Удобно для «этот блок убивает», «этот сундук открывается».
Полезное: game.log(...) печатает в консоль (значок
«Консоль» внизу) — для отладки. game.player.position
— где сейчас игрок ({'{x, y, z}'}).
Самое частое событие — игрок коснулся объекта.
{`local part = script.Parent
-- Игрок наступил
part.Touched:Connect(function(hit)
local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
if player then
print("Ты коснулся плиты!")
end
end)
-- Игрок ушёл
part.TouchEnded:Connect(function(hit)
local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
if player then
print("Отошёл")
end
end)`}}
/>
Подписаться на чужой объект из глобального скрипта:
{`local trap = workspace:WaitForChild("Ловушка")
trap.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then humanoid:TakeDamage(20) end
end)`}}
/>
>
),
},
{
id: 'recipes-killblock',
title: 'S3. Килблок — урон и смерть при касании',
body: (
<>
Килблок — объект, который наносит урон или мгновенно убивает при касании (лава, шипы, кислота).
{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid.Health = 0 -- мгновенная смерть
end
end)`}}
/>
Если хочешь не убивать сразу, а наносить урон:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then humanoid:TakeDamage(25) end
end)`}}
/>
Постоянный урон, пока игрок стоит в зоне:
{`local part = script.Parent
local inside = {} -- humanoid → true
part.Touched:Connect(function(hit)
local h = hit.Parent:FindFirstChild("Humanoid")
if h then inside[h] = true end
end)
part.TouchEnded:Connect(function(hit)
local h = hit.Parent:FindFirstChild("Humanoid")
if h then inside[h] = nil end
end)
-- Урон каждые 0.5 сек пока стоит
while true do
task.wait(0.5)
for h in pairs(inside) do
if h.Parent then h:TakeDamage(5) end
end
end`}}
/>
Предмет исчезает при касании — основа сбора монет.
{`local part = script.Parent
part.Touched:Connect(function(hit)
local h = hit.Parent:FindFirstChild("Humanoid")
if h then
part:Destroy()
end
end)`}}
/>
Со счётчиком: монетка увеличивает leaderstats игрока:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
if not player then return end
-- Прибавить монетку в leaderstats
local stats = player:FindFirstChild("leaderstats")
if stats and stats:FindFirstChild("Монеты") then
stats.Монеты.Value = stats.Монеты.Value + 1
end
part:Destroy()
end)`}}
/>
JS: глобальный скрипт принимает broadcast и считает:
{`-- В Lua счёт уже в leaderstats игрока (см. код на монетке выше).
-- Проверим достижение цели в глобальном скрипте:
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
-- Создаём leaderstats папку при заходе
local stats = Instance.new("Folder", player)
stats.Name = "leaderstats"
local coins = Instance.new("IntValue", stats)
coins.Name = "Монеты"
coins.Value = 0
coins.Changed:Connect(function(newVal)
if newVal >= 10 then
print("Собрал все!")
end
end)
end)`}}
/>
>
),
},
{
id: 'recipes-teleport',
title: 'S5. Телепорт и смена позиции при касании',
body: (
<>
При касании переместить игрока (портал) или сдвинуть сам объект.
Портал — телепорт игрока:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local h = hit.Parent:FindFirstChild("Humanoid")
if not h then return end
local hrp = hit.Parent:FindFirstChild("HumanoidRootPart")
if hrp then
hrp.CFrame = CFrame.new(0, 20, 50)
end
end)`}}
/>
Сдвинуть сам объект при касании (опустить мост):
{`local part = script.Parent
local opened = false
part.Touched:Connect(function(hit)
if opened then return end
if not hit.Parent:FindFirstChild("Humanoid") then return end
opened = true
part.Position = part.Position - Vector3.new(0, 3, 0)
end)`}}
/>
Плавно сдвинуть — через TweenService:
{`local TweenService = game:GetService("TweenService")
local part = script.Parent
part.Touched:Connect(function(hit)
if not hit.Parent:FindFirstChild("Humanoid") then return end
local goal = { Position = part.Position + Vector3.new(4, 0, 0) }
TweenService:Create(part, TweenInfo.new(1), goal):Play()
end)`}}
/>
>
),
},
{
id: 'recipes-primitive-props',
title: 'S6. Все свойства примитивов из скрипта',
body: (
<>
Любой примитив можно создать и менять из скрипта.
Создать примитив:
{`local box = Instance.new("Part")
box.Name = "МойКуб"
box.Shape = Enum.PartType.Block
box.Size = Vector3.new(2, 1, 3)
box.Position = Vector3.new(0, 2, 0)
box.Orientation = Vector3.new(0, math.deg(0.8), 0) -- градусы
box.Color = Color3.fromRGB(255, 85, 51)
box.Material = Enum.Material.Neon
box.Anchored = true
box.CanCollide = true
box.Transparency = 0
-- Если Anchored=false: box.Mass читается, не задаётся.
-- Управляется через PhysicalProperties и Density.
box.Parent = workspace`}}
/>
Типы примитивов:
{`Enum.PartType.Block / Ball / Cylinder / Wedge / CornerWedge
-- Для cone/pyramid/torus используются MeshPart или SpecialMesh:
local sphere = Instance.new("Part")
sphere.Shape = Enum.PartType.Ball -- сфера`}}
/>
Менять свойства существующего объекта:
{`-- Прямое присваивание свойств Part
box.Color = Color3.fromRGB(0, 255, 136)
box.Material = Enum.Material.Glass
box.Transparency = 0.6 -- 0=видно, 1=невидимо
box.CanCollide = false
box.Size = Vector3.new(3, 1, 1)
box.Position = Vector3.new(5, 2, 0)
box.Orientation = Vector3.new(0, 90, 0)
-- Скрыть: Transparency = 1 (или Parent = nil)
box:Destroy() -- удалить`}}
/>
Math.PI/2 ≈ 1.57.
}
lua={math.rad(90).
Вращающийся объект (монета, портал):
{`local RunService = game:GetService("RunService")
local part = script.Parent
local angle = 0
RunService.Heartbeat:Connect(function(dt)
angle = angle + dt * 2
part.CFrame = CFrame.new(part.Position) * CFrame.Angles(0, angle, 0)
end)`}}
/>
Парение вверх-вниз:
{`local RunService = game:GetService("RunService")
local part = script.Parent
local startPos = part.Position
local t = 0
RunService.Heartbeat:Connect(function(dt)
t = t + dt
local dy = math.sin(t * 2) * 0.4
part.Position = Vector3.new(startPos.X, startPos.Y + dy, startPos.Z)
end)`}}
/>
Пульсация размера:
{`local TweenService = game:GetService("TweenService")
local part = script.Parent
local origSize = part.Size
local info = TweenInfo.new(
0.6, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut,
-1, -- бесконечно
true -- yoyo (туда-обратно)
)
local goal = { Size = Vector3.new(origSize.X, origSize.Y * 1.4, origSize.Z) }
TweenService:Create(part, info, goal):Play()`}}
/>
Мигание цветом:
{`local part = script.Parent
local on = false
while true do
task.wait(0.5)
on = not on
part.Color = on and Color3.fromRGB(255, 0, 0)
or Color3.fromRGB(51, 0, 0)
end`}}
/>
>
),
},
{
id: 'recipes-button-door',
title: 'S8. Кнопка по E и дверь',
body: (
<>
Взаимодействие по клавише E:
{`local part = script.Parent
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = "Открыть"
prompt.MaxActivationDistance = 4
prompt.KeyboardKeyCode = Enum.KeyCode.E
prompt.Parent = part
-- BindableEvent для оповещения "open-door"
local doorEvent = workspace:FindFirstChild("DoorOpenEvent")
or Instance.new("BindableEvent", workspace)
doorEvent.Name = "DoorOpenEvent"
prompt.Triggered:Connect(function(player)
print("Открыто!")
doorEvent:Fire()
end)`}}
/>
На двери:
{`local TweenService = game:GetService("TweenService")
local door = script.Parent
local closedPos = door.Position
local doorEvent = workspace:WaitForChild("DoorOpenEvent")
doorEvent.Event:Connect(function()
local goal = { Position = closedPos + Vector3.new(0, 4, 0) }
TweenService:Create(door, TweenInfo.new(1), goal):Play()
door.CanCollide = false
end)`}}
/>
holdDuration: 1 в onInteract / prompt.HoldDuration = 1
в Roblox — держать E одну секунду.
HUD-надписи:
{`-- В Roblox HUD = leaderstats папка (см. G7) или свой ScreenGui
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local gui = player:WaitForChild("PlayerGui")
-- Своя метка по центру
local screen = Instance.new("ScreenGui", gui)
local label = Instance.new("TextLabel", screen)
label.Size = UDim2.new(0.4, 0, 0.1, 0)
label.Position = UDim2.new(0.3, 0, 0.4, 0)
label.Text = "Старт!"
label.TextScaled = true
label.BackgroundTransparency = 0.5
task.delay(2, function() screen:Destroy() end)`}}
/>
Обратный отсчёт:
{`local time = 30
while time > 0 do
task.wait(1)
time = time - 1
print("Осталось: " .. time)
end
print("Время вышло!")
-- Убить локального игрока
local player = game:GetService("Players").LocalPlayer
if player.Character and player.Character:FindFirstChild("Humanoid") then
player.Character.Humanoid.Health = 0
end`}}
/>
Кнопка GUI:
{`local player = game:GetService("Players").LocalPlayer
local gui = player:WaitForChild("PlayerGui")
local screen = Instance.new("ScreenGui", gui)
local btn = Instance.new("TextButton", screen)
btn.Size = UDim2.new(0.2, 0, 0.08, 0)
btn.Position = UDim2.new(0.4, 0, 0.8, 0)
btn.Text = "НАЧАТЬ"
btn.BackgroundColor3 = Color3.fromRGB(58, 110, 224)
btn.TextColor3 = Color3.new(1, 1, 1)
btn.MouseButton1Click:Connect(function()
print("Поехали!")
btn.Visible = false
end)`}}
/>
>
),
},
{
id: 'recipes-spawn-fall',
title: 'S10. Спавн, падение, проверка падения вниз',
body: (
<>
Спавнить объекты с неба каждую секунду:
{`local Debris = game:GetService("Debris")
while true do
task.wait(1)
local x = math.random(-10, 10)
local ball = Instance.new("Part")
ball.Shape = Enum.PartType.Ball
ball.Size = Vector3.new(1, 1, 1)
ball.Position = Vector3.new(x, 20, 0)
ball.Color = Color3.fromRGB(255, 215, 0)
ball.Material = Enum.Material.Neon
ball.Anchored = false -- падает
ball.Parent = workspace
Debris:AddItem(ball, 8) -- удалить через 8 сек
end`}}
/>
Игрок упал вниз:
{`local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function()
local player = Players.LocalPlayer
if not player.Character then return end
local hrp = player.Character:FindFirstChild("HumanoidRootPart")
if hrp and hrp.Position.Y < -10 then
player:LoadCharacter() -- респавн
print("Упал!")
end
end)`}}
/>
Финиш:
{`local part = script.Parent
part.Touched:Connect(function(hit)
local h = hit.Parent:FindFirstChild("Humanoid")
if not h then return end
print("ПОБЕДА!")
h.WalkSpeed = 0 -- заморозить
h.JumpPower = 0
end)`}}
/>
>
),
},
{
id: 'recipes-npc-enemy',
title: 'S11. Враг, который идёт за игроком',
body: (
<>
NPC/враг, преследующий игрока:
{`-- Враг должен быть Model с Humanoid и HumanoidRootPart в workspace.
-- Например workspace.Зомби.
local Players = game:GetService("Players")
local enemy = workspace:WaitForChild("Зомби")
local humanoid = enemy:WaitForChild("Humanoid")
local hrp = enemy:WaitForChild("HumanoidRootPart")
-- Преследование игрока
task.spawn(function()
while enemy.Parent and humanoid.Health > 0 do
local player = Players:GetPlayers()[1]
if player and player.Character then
local target = player.Character:FindFirstChild("HumanoidRootPart")
if target then
humanoid:MoveTo(target.Position)
end
end
task.wait(0.5)
end
end)
humanoid.Died:Connect(function()
print("Враг повержен!")
end)
-- Урон когда близко
task.spawn(function()
while enemy.Parent and humanoid.Health > 0 do
task.wait(0.5)
local player = Players:GetPlayers()[1]
if player and player.Character then
local target = player.Character:FindFirstChild("HumanoidRootPart")
local playerHum = player.Character:FindFirstChild("Humanoid")
if target and playerHum then
local dist = (target.Position - hrp.Position).Magnitude
if dist < 2 then
playerHum:TakeDamage(10)
end
end
end
end
end)`}}
/>
>
),
},
{
id: 'recipes-save',
title: 'S12. Сохранение прогресса и лидерборд',
body: (
<>
Лидерборд:
{`-- leaderstats: см. G7
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
local stats = Instance.new("Folder", player)
stats.Name = "leaderstats"
local coins = Instance.new("IntValue", stats)
coins.Name = "Монеты"
coins.Value = 0
end)
-- Прибавить монетку (например, при сборе)
local function addCoin(player, amount)
local stats = player:FindFirstChild("leaderstats")
if stats then
stats.Монеты.Value = stats.Монеты.Value + amount
end
end`}}
/>
Сохранение между сессиями:
{`-- В Roblox сохранение через DataStoreService (требует онлайн-игру)
local DataStoreService = game:GetService("DataStoreService")
local progress = DataStoreService:GetDataStore("Progress")
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
-- Прочитать при входе
local success, data = pcall(function()
return progress:GetAsync(player.UserId)
end)
if success and data then
print("С возвращением! Уровень " .. (data.level or 1))
-- Применить прогресс: leaderstats.Монеты.Value = data.coins и т.п.
end
end)
Players.PlayerRemoving:Connect(function(player)
-- Сохранить при выходе
local data = {
level = 3,
coins = 10,
bestScore = 5000,
}
pcall(function()
progress:SetAsync(player.UserId, data)
end)
end)`}}
/>
Совместное редактирование (Team Create) — это когда одну игру в студии редактируют несколько человек одновременно. Ты строишь дом, друг в это же время ставит деревья — и каждый видит правки другого вживую, без перезагрузки страницы.
Что синхронизируется в реальном времени:
Открой свою игру в студии и перейди на вкладку Игра в верхней панели. Там, в группе «Вместе», есть кнопка Пригласить.
Блокировка объекта. Пока один соавтор выделил объект и двигает его — этот объект заблокирован для других (никто другой не сможет его двигать одновременно). Так правки не конфликтуют. Как только выделение снято — объект снова свободен.
Кто сохраняет. Игру в базу сохраняет автор (владелец проекта). Приглашённые друзья видят пометку «Совместное редактирование» вместо кнопок Сохранить и Опубликовать — это правильно: они помогают строить, а управляет игрой автор.
Цвета. У каждого соавтора свой цвет — им подсвечены его курсор и аватарка, чтобы было понятно, кто что делает.
Совет: договоритесь заранее, кто какую часть карты делает (например, один — здания, другой — ландшафт и декор) — так работать вместе быстрее и без накладок.
> ), }, ], }, // ════════════════════════════════════════════════════ // РАЗДЕЛ — КОНТЕКСТ ДЛЯ НЕЙРОНКИ (AI) // ════════════════════════════════════════════════════ { id: 'ai-context', icon: 'lightbulb', title: 'Контекст для нейронки', summary: 'Готовый текст со всем API скриптов Рублокса. Скопируй его целиком, вставь в ChatGPT/нейросеть — и она будет писать тебе рабочие скрипты под твою задачу.', sections: [ { id: 'ai-howto', title: 'AI1. Как писать скрипты с нейросетью', body: ( <>Хочешь, чтобы скрипт написала нейросеть (ChatGPT, DeepSeek, Claude и т.п.)? Проблема в том, что нейросеть не знает устройство Рублокса — придумает несуществующие команды. Решение простое:
Выдели весь текст ниже и скопируй (Ctrl+A внутри блока или мышью), затем вставь в нейросеть перед своим вопросом:
{AI_CONTEXT}
>
),
},
],
},
];