Script и LocalScript в Roblox: в чём разница и где их использовать
В Roblox есть несколько типов скриптов, но чаще всего новичок упирается в два: Script и LocalScript. Они похожи по синтаксису Luau, но запускаются в разных местах: один — на сервере, другой — на устройстве игрока.
Правильный выбор зависит от вопроса: кто должен «решать» действие (сервер или клиент), кто должен это видеть (все или один игрок) и какие данные можно считать надёжными.
В этом гайде — логика выбора Script/LocalScript, правильные папки в Explorer, базовые правила связи через RemoteEvent и список типовых ошибок, когда «скрипт не работает».
Если вы используете новые настройки RunContext, поведение Script может отличаться от старых уроков. В статье отдельно показано, как это влияет на запуск кода.
- Короткий ответ: чем Script отличается от LocalScript
- Где запускаются скрипты: сервер, клиент и RunContext
- Где размещать скрипты в Explorer
- Как общаться клиенту и серверу: RemoteEvent и RemoteFunction
- Безопасность: почему клиенту нельзя доверять
- Шаблоны: типовые задачи и где писать код
- Типовые ошибки и диагностика
Короткий ответ: чем Script отличается от LocalScript
Script выполняется на сервере Roblox и может менять мир так, чтобы изменения увидели все игроки. LocalScript выполняется на клиенте (у конкретного игрока) и обычно отвечает за интерфейс, ввод, камеру и локальные эффекты.
Быстрое правило выбора
- Если действие влияет на других игроков, награды, предметы, сохранения — выбирайте сервер (Script).
- Если действие нужно только одному игроку (UI, подсказки, управление камерой) — выбирайте клиент (LocalScript).
Таблица сравнения
| Вопрос | Script | LocalScript |
|---|---|---|
| Где запускается | Сервер | Клиент игрока |
| Кто видит результат | Все (через серверную репликацию) | Обычно только один игрок |
| Можно ли доверять входным данным | Да, если данные получены/проверены на сервере | Нет, клиент можно подменить |
| Типичные задачи | Спавн предметов, урон, покупки, сохранения | UI, кнопки, эффекты, локальные анимации |
А где тут ModuleScript
ModuleScript — это способ хранить общий код и подключать его через require. Он не «серверный» и не «клиентский» сам по себе: контекст зависит от того, кто его вызвал и где он лежит.
Где запускаются скрипты: сервер, клиент и RunContext
У Roblox есть граница «клиент ↔ сервер». Сервер считается авторитетным: он решает, что действительно произошло. Клиент отвечает за удобство и мгновенную реакцию, но его данные нельзя считать честными без проверки.
RunContext: почему «обычный Script» иногда ведёт себя как LocalScript
У базового класса скриптов есть свойство RunContext, которое определяет, в каком контексте выполняется код. В старой модели (legacy) поведение сильно зависело от типа скрипта и места в дереве. В новой модели можно явно задать контекст для Script.
- Если RunContext настроен на сервер, код работает как обычный серверный Script.
- Если RunContext настроен на клиент, Script выполняется на клиенте и по смыслу становится похож на LocalScript, но может использоваться в других местах модели.
Не используйте клиентский RunContext как способ «спрятать логику». Любой код, попавший на клиент, можно прочитать и изменить, поэтому важные решения всё равно должны быть на сервере.
Почему важно знать контекст прямо сейчас
Когда вы переносите код между папками или меняете RunContext, могут меняться доступные объекты и порядок запуска. Если у вас внезапно перестали работать события или появились nil, первым делом проверьте: где лежит скрипт и какой у него контекст.
Где размещать скрипты в Explorer
Даже правильный по смыслу скрипт не запустится, если лежит «не там». У Roblox есть контейнеры, которые автоматически копируются игрокам или доступны только серверу.
Рекомендуемые папки
| Куда положить | Что там обычно лежит | Зачем |
|---|---|---|
| ServerScriptService | Script, ModuleScript | Серверная логика, недоступная клиенту |
| ServerStorage | Шаблоны моделей, ресурсы | Хранение того, что не нужно клиенту |
| ReplicatedStorage | RemoteEvent, RemoteFunction, общие ModuleScript | Доступно и серверу, и клиенту |
| StarterPlayerScripts | LocalScript | Код, который запускается у каждого игрока при входе |
| StarterCharacterScripts | LocalScript | Код, который копируется в персонажа при каждом спавне |
| StarterGui | UI и LocalScript внутри UI | UI копируется в PlayerGui, там и запускается LocalScript |
Что нельзя делать (частые ловушки)
- Класть LocalScript в Workspace и ждать, что он запустится. Обычно он не выполняется, если не находится внутри персонажа игрока.
- Пытаться выполнять серверный Script в папках, которые копируются игроку (например, в StarterGui). Эти контейнеры не предназначены для серверного кода.
Мини-чеклист перед отладкой
- Скрипт лежит в правильном контейнере?
- Включён ли он (Enabled) и не остановлен ли ошибкой?
- Не зависит ли он от объекта, который создаётся позже?
Как общаться клиенту и серверу: RemoteEvent и RemoteFunction
Клиент и сервер не делят одну память. Если игрок нажал кнопку в интерфейсе, сервер узнает об этом только если вы отправите сообщение через специальные объекты сети.
Что использовать: RemoteEvent или RemoteFunction
| Инструмент | Как работает | Когда подходит |
|---|---|---|
| RemoteEvent | Однонаправленно, без ожидания ответа | Сигналы: «кнопка нажата», «удар нанесён», «хочу купить» |
| RemoteFunction | Двусторонне, с ожиданием ответа | Редкие запросы, где нужен немедленный ответ и вы контролируете нагрузку |
Где хранить remotes
RemoteEvent/RemoteFunction должны лежать там, где их видят обе стороны. Чаще всего для этого используют ReplicatedStorage.
Серверная проверка: минимальный стандарт
Клиент может отправить любые данные. Поэтому сервер должен проверять вход: диапазоны чисел, допустимые предметы, расстояние до объекта, частоту вызовов и права игрока.
| Что происходит | Вероятная причина | Что сделать |
|---|---|---|
| Игрок «покупает» предмет без денег | Сервер доверяет числу из клиента | Храните баланс на сервере и пересчитывайте итог на сервере |
| RemoteEvent спамят сотни раз | Нет лимитов частоты | Добавьте rate limit и банально игнорируйте лишнее |
| Игрок активирует объект на другом конце карты | Нет проверки позиции/дистанции | Проверьте расстояние на сервере, а не на клиенте |
Следующая важная тема: RemoteEvent и RemoteFunction.
Безопасность: почему клиенту нельзя доверять
Короткая формулировка, которая экономит недели: клиент нельзя считать честным. Всё, что запускается у игрока, можно подменить: значения, вызовы remotes, порядок действий.
Что именно считается «недоверенным»
- Любые числа, которые клиент прислал на сервер (урон, скорость, цену, награду).
- Любые «факты», которые клиент утверждает (я рядом с магазином, я попал по цели).
- Любой код, который оказался в доступных клиенту контейнерах (его можно прочитать).
Как проектировать логику безопаснее
- Сервер хранит истину: инвентарь, валюту, прогресс, выдачу наград.
- Клиент просит, сервер решает: клиент отправляет запрос, сервер проверяет и выполняет.
- Сервер вычисляет критичные вещи сам: например, итоговую цену или награду.
- Проверяйте контекст: дистанция, кулдаун, состояние персонажа, допустимые предметы.
Если вы делаете античит, не пытайтесь «спрятать» его в LocalScript. Смысл античита — серверные проверки, а не секретный код на клиенте.
Где клиент действительно нужен
Клиент полезен там, где важна мгновенная реакция: анимации интерфейса, эффекты, подсказки, предварительный прицел. Но итог (попадание, урон, покупка) лучше подтверждать сервером.
Шаблоны: Типичные задачи и где писать код
Ниже — типовые задачи и где их удобнее реализовывать. Это не единственно верный путь, но хороший старт для структуры проекта.
Таблица: задача → где код → почему
| Задача | Где писать | Почему так |
|---|---|---|
| Кнопка UI «Купить» | LocalScript (UI) + Script (сервер) через RemoteEvent | UI живёт у игрока, а списание валюты и выдача предмета должны быть на сервере |
| Подсказка на экране, уведомления | LocalScript | Это локальный эффект, другим игрокам не нужен |
| Спавн предмета в мире | Script | Изменение мира должно быть серверным, иначе его не увидят все |
| Локальная камера/ввод | LocalScript | Доступ к камере и вводу — клиентская зона |
| Сохранение прогресса | Script | Сохранения должны выполняться на сервере |
Мини-шаблон структуры (без кода)
- UI-кнопка вызывает RemoteEvent с минимальными данными (например, id товара).
- Серверный обработчик проверяет: существует ли товар, хватает ли валюты, нет ли спама.
- Сервер изменяет состояние (валюта/инвентарь) и при необходимости сообщает клиенту результатом.
Если вы хотите вынести бизнес-логику в общий модуль, используйте ModuleScript и держите его либо в ServerScriptService (только сервер), либо в ReplicatedStorage (если нужен доступ клиенту и серверу).
Если модуль лежит в ReplicatedStorage, исходники модуля также окажутся на клиенте. Не храните там секреты и критичные правила без серверной проверки.
Ошибки и диагностика
Если «ничего не происходит», почти всегда проблема в одном из четырёх пунктов: контекст, место в дереве, порядок запуска, или сеть (remotes).
Таблица: Что происходит → причина → что сделать
| Что происходит | Вероятная причина | Что сделать |
|---|---|---|
| LocalScript не печатает даже print | Скрипт лежит в месте, где клиент не запускает LocalScript | Переместите в StarterPlayerScripts / StarterCharacterScripts / PlayerGui / Backpack |
| LocalScript в StarterGui «не работает» | Он запускается уже в PlayerGui после копирования | Проверяйте путь и ищите его в PlayerGui во время игры |
| OnClientEvent/OnServerEvent не срабатывает | RemoteEvent лежит в месте, недоступном одной стороне | Положите remote в ReplicatedStorage и убедитесь, что имя совпадает |
| nil у LocalPlayer.Character или его частей | Персонаж ещё не создан или идёт респавн | Ждите появления Character/ChildAdded и учитывайте респавн |
| Серверный Script «как будто не видит» изменения клиента | Клиентские изменения не реплицируются на сервер | Передавайте намерение через remotes и применяйте изменения на сервере |
Что проверить сначала
- Откройте Output и посмотрите первые ошибки (красные строки).
- Проверьте, что скрипт запущен в нужном контексте (сервер/клиент) и в правильной папке.
- Если есть remote: проверьте его место (обычно ReplicatedStorage) и имена обработчиков.
- Если ошибка связана с персонажем: добавьте ожидание появления Character и нужных частей.
Типовая структура отладки
- Добавьте по одному простому print в ключевых местах и убедитесь, что путь выполнения вообще проходит там.
- Если код на клиенте, смотрите клиентский Output; если на сервере — серверный.
- Если проблема сетевой: временно логируйте аргументы, которые приходят в обработчик на сервере.