Для меню Roblox создайте ScreenGui в StarterGui, добавьте Frame и TextButton, а обработчик кнопки поместите в LocalScript. Используйте событие Activated, размеры через Scale, AnchorPoint для привязки и UISizeConstraint, чтобы меню оставалось удобным на телефоне и компьютере.
Содержимое StarterGui копируется в PlayerGui каждого игрока, а экранный интерфейс работает на клиенте. Кнопка не должна сама менять монеты, выдавать предмет или подтверждать победу. Она управляет локальным видом и при необходимости отправляет серверу узкий запрос через RemoteEvent. Такое разделение делает меню быстрым, а игровые правила защищёнными.
Контейнер экранного интерфейса в PlayerGui.
Единое событие клика и касания для TextButton и ImageButton.
Гибкий размер с разумным минимумом и максимумом.

ScreenGui Roblox: меню и кнопки с плавным открытием
Создайте ScreenGui с именем MainGui, внутри Frame Menu и TextButton OpenButton. У Frame задайте AnchorPoint 0.5, 0.5 и Position 0.5, 0, 0.5, 0. Скрипт ниже находится внутри ScreenGui. Он меняет масштаб через UIScale, поэтому анимация не ломает рассчитанное положение.
local TweenService = game:GetService("TweenService")
local gui = script.Parent
local menu = gui:WaitForChild("Menu")
local button = gui:WaitForChild("OpenButton")
local scale = menu:FindFirstChildOfClass("UIScale") or Instance.new("UIScale")
scale.Parent = menu
menu.Visible = false
local function setMenuOpen(isOpen)
menu.Visible = true
scale.Scale = isOpen and 0.92 or 1
local tween = TweenService:Create(
scale,
TweenInfo.new(0.18, Enum.EasingStyle.Quad),
{ Scale = isOpen and 1 or 0.92 }
)
tween:Play()
if not isOpen then
tween.Completed:Once(function()
menu.Visible = false
end)
end
end
button.Activated:Connect(function()
setMenuOpen(not menu.Visible)
end)- Первое нажатие: Frame появляется и увеличивается до 100%
- Повторное нажатие: Frame уменьшается и скрывается
Для реального меню добавьте отдельную кнопку закрытия и флаг состояния, потому что Visible меняется только после завершения закрывающей анимации. Во время tween временно блокируйте повторное нажатие или отменяйте предыдущую анимацию. Если кнопка запускает покупку, LocalScript показывает ожидание и отправляет itemName через RemoteEvent, а успех получает обратно.
Offset, Scale и ограничения
| Элемент | Что означает | Что делать |
|---|---|---|
| Scale | Доля размера родителя | Основной адаптивный размер и позиция |
| Offset | Фиксированные пиксели | Отступы, тонкие линии, минимальные детали |
| AnchorPoint | Точка привязки 0..1 | Центрирование и привязка к краям |
| UISizeConstraint | MinSize и MaxSize | Защита от слишком маленького или большого окна |
| UIAspectRatioConstraint | Соотношение сторон | Карточки, квадратные иконки |
Не пытайтесь сделать весь интерфейс только на Scale. На очень широком мониторе панель станет огромной, а на маленьком телефоне слишком тесной. Комбинируйте относительную основу с ограничениями и внутренними отступами. В Studio включайте эмуляцию разных устройств, затем проверяйте опубликованную версию на физическом телефоне.

Практика: соберите MainGui и добейтесь плавного открытия Menu
Откройте пустой Baseplate и соберите меню с нуля: ScreenGui с именем MainGui в StarterGui, внутри Frame Menu и TextButton OpenButton. Успех выглядит так: первое нажатие показывает Frame и растягивает его с 0.92 до полного размера за 0.18 секунды, повторное нажатие сжимает и прячет. Весь код лежит в LocalScript внутри MainGui, серверный Script здесь не нужен.
- ScreenGui MainGui в StarterGui. Вставьте ScreenGui в StarterGui, переименуйте в MainGui и положите внутрь Frame с именем Menu и TextButton с именем OpenButton. Нажмите Play и найдите в проводнике копию MainGui внутри Players, вашего игрока и PlayerGui.
- AnchorPoint и Position у Frame. Задайте у Menu AnchorPoint 0.5, 0.5 и Position 0.5, 0, 0.5, 0, а размер наберите долями Scale, не пикселями Offset. Потяните окно Studio за угол: Frame остаётся по центру и меняет размер вместе с окном.
- LocalScript и событие Activated. Положите в MainGui LocalScript, подпишитесь на button.Activated и переключайте в обработчике menu.Visible. Запустите игру и нажмите OpenButton: Frame появляется и пропадает мгновенно, анимации пока нет.
- UIScale через TweenService. Добавьте в Menu объект UIScale и анимируйте его свойство Scale от 0.92 к 1 вызовом TweenService:Create с TweenInfo.new(0.18, Enum.EasingStyle.Quad). Открытие превращается в рост из центра, а рассчитанное положение Frame не сдвигается.
- Скрытие по tween.Completed. На закрытии переносите menu.Visible = false внутрь tween.Completed:Once, иначе Frame исчезнет в первом же кадре анимации. Нажмите кнопку два раза подряд: меню сначала досжимается до 0.92, потом пропадает.
Теперь сломайте меню намеренно. Замените Scale в Position и Size на крупные значения Offset и включите в Studio эмуляцию телефона: панель, собранная под окно ноутбука, вылезет за край экрана или займёт его целиком, потому что пиксели не пересчитываются под другое разрешение. Второй эксперимент: жмите OpenButton несколько раз подряд, пока идёт закрывающий tween. Функция setMenuOpen читает menu.Visible, а он ещё равен true, поэтому состояние собьётся: реальному меню нужен отдельный флаг и блокировка кнопки на время анимации.
Дальше добавьте в Menu отдельный CloseButton и флаг состояния, чтобы открытие и закрытие не зависели от текущего значения Visible. Повесьте UIAspectRatioConstraint на карточки внутри меню, тогда квадратные иконки не расплющатся на широком мониторе. Если кнопка запускает покупку, пусть LocalScript показывает ожидание и отправляет itemName через RemoteEvent, а результат подтверждает сервер.
Частые ошибки и почему они появляются
Интерфейс часто выглядит хорошо только в том окне, где его собирали. Причина обычно в больших Offset для Position и Size. Вторая проблема появляется, когда один LocalScript знает обо всех экранах и серверных правилах сразу. Третья связана с кнопками: MouseButton1Click работает для мыши, а Activated удобнее для нескольких способов ввода.
Экранное меню принадлежит конкретному клиенту. Анимацию и локальное состояние держите в LocalScript.
Клиент отправляет запрос, но изменение общего состояния подтверждает сервер.
Эмуляция и реальный телефон должны входить в критерий готовности, а не быть последней случайной проверкой.
Самопроверка
Чем Activated лучше MouseButton1Click для кнопки в Roblox?
Activated срабатывает на клик мышью, на тап по экрану и на другие способы активации TextButton или ImageButton. MouseButton1Click ловит только мышь, поэтому на телефоне и геймпаде такая кнопка останется мёртвой.
Где искать ScreenGui во время игры, если он лежит в StarterGui?
Во время игры ScreenGui лежит в PlayerGui конкретного игрока: содержимое StarterGui копируется туда для каждого. В проводнике при запущенном тесте ищите MainGui по пути Players, ваш игрок, PlayerGui.
Что ограничивает максимальный размер меню на широком мониторе?
Максимальный размер меню ограничивает UISizeConstraint со свойством MaxSize, а MinSize не даёт панели схлопнуться на маленьком телефоне. Один только Scale так не умеет: доля от родителя растёт вместе с монитором.
Почему интерфейс разъезжается на телефоне, хотя в Studio выглядел нормально?
Интерфейс разъезжается, когда Position и Size набраны большими значениями Offset: фиксированные пиксели не пересчитываются под другое разрешение. Соберите основу на Scale, оставьте Offset для отступов и тонких линий, затем проверьте макет в эмуляции устройств и на физическом телефоне.
Можно ли двигать элементы ScreenGui серверным скриптом?
Двигать элементы ScreenGui серверным Script не нужно: экранное меню принадлежит конкретному клиенту, а анимация и локальное состояние живут в LocalScript. Сервер подключают, только когда кнопка меняет общее состояние игры, и запрос к нему идёт через RemoteEvent.
Почему меню исчезает раньше, чем доигрывает анимация закрытия?
Меню исчезает раньше времени, когда menu.Visible = false стоит сразу после tween:Play(): Frame пропадает в том же кадре, а анимация доигрывает уже невидимый объект. Перенесите эту строку в tween.Completed:Once, и меню сначала сожмётся до 0.92, а потом скроется.
Что делать дальше
Соберите одно меню из Frame, заголовка, кнопки закрытия и действия. Проверьте четыре размера экрана и навигацию с клавиатуры или геймпада, если она нужна игре. Логику функций и событий можно повторить на курсе Lua. Для серверного действия подключите RemoteEvent, а для награды изучите выдачу Tool.
