Разработка / ROBLOX
ProximityPrompt в Roblox: учебная станция с E, геймпадом и touch
Создайте отдельную закреплённую лампу с Attachment и ProximityPrompt. Понятные подписи, условия видимости, серверная проверка и план испытаний на разных устройствах.
Одна станция, одно понятное действие #
Представьте отдельный прототип: игрок подходит к небольшой лампе и переключает её свет. Вместо поиска невидимой кнопки он видит имя объекта, действие и подходящую подсказку ввода. Мы используем стандартный ProximityPrompt и обычный серверный Script. Ни наград, ни валюты, ни покупок в примере нет.
Это самостоятельное упражнение, а не исправление Paws and Finds, Cloud Nests или других игр автора. Код ниже написан для описанной структуры. Его статическая проверка не означает запуск в движке Studio. Сначала сохраните отдельный учебный проект, соберите имена точно по схеме и определите ожидаемый результат: меняются общий свет и цвет станции, а не личный инвентарь.
1. Соберите точную структуру в Explorer #
В Workspace добавьте Part с именем PracticeStation. Внутри него нужны Attachment с именем PromptAnchor и PointLight с именем PracticeLight. В PromptAnchor добавьте ProximityPrompt с именем SwitchPrompt. Это две ветки: свет — ребёнок детали, подсказка — ребёнок вложенного Attachment. Не кладите Prompt рядом с Attachment по привычке.
В ServerScriptService создайте обычный Script PracticeStationServer, включите его и установите RunContext в Server. Не заменяйте его LocalScript. Имена объектов в коде чувствительны к написанию. WaitForChild ждёт конкретное имя, а не создаёт недостающий объект. При ожидании сначала сравните дерево Explorer со схемой: исправлять логику расстояния бессмысленно, если SwitchPrompt находится в другой ветке.
2. Закрепите лампу и поставьте точку подсказки #
Для упражнения задайте PracticeStation: Anchored=true, Size=(4,2,4), Position=(0,3,0). Эти размеры и координаты — наши учебные значения, а не требование движка. Поставьте SpawnLocation рядом, вне детали, чтобы персонаж мог подойти. У PromptAnchor задайте Position=(0,2,0): это локальная позиция относительно станции, не самостоятельная мировая координата.
У PracticeLight можно выбрать Brightness=2 и Range=12 как стартовые значения. Скрипт начинает с выключенного света. Не добавляйте сложную модель, подвижные соединения или готовую систему наград ради этой проверки. Закреплённая деталь даёт стабильный ориентир; сервер затем измеряет расстояние до anchor.WorldPosition. Если переставляете Attachment, меняется точка взаимодействия, поэтому повторите проверку границы.
3. Подпишите объект и действие своим языком #
ObjectText отвечает на вопрос «что это?», ActionText — «что я сделаю?». Для русской сцены мы выбрали «Учебная лампа» и «Переключить свет». Код содержит пары для ru/en/de/es/zh/ar. В UI_LANGUAGE выберите язык этой учебной сцены: например, "ru". Названия PracticeStation и SwitchPrompt в Explorer при этом не переводятся.
Это один настроенный язык для всего тестового сервера, а не автоматический язык каждого игрока. AutoLocalize=false оставляет выбранные строки без автоматической подмены. Для настоящей многоязычной игры отдельно проектируют таблицу локализации и проверяют длинные строки. Важнее сохранить смысл действия, чем вписать E в ActionText: клавишу или экранный ввод показывает стандартная подсказка в зависимости от способа управления.
4. Разделите клавиатуру, геймпад и экранное нажатие #
В примере KeyboardKeyCode=E, GamepadKeyCode=ButtonX, ClickablePrompt=true. Не обещайте, что каждый пользователь увидит букву E: актуальный тип ввода влияет на стандартное отображение. На телефоне или планшете подсказка доступна для экранного нажатия; даже ClickablePrompt=false не запрещает мобильное нажатие. Этот параметр не является выключателем touch-доступа.
HoldDuration=0.3 задаёт удержание в обычном интерфейсе; 0 означало бы немедленное срабатывание. Значение 0.3 выбрано для упражнения и не измерено как лучший вариант. Попробуйте короткое и полное удержание, затем отпустите кнопку. Не используйте начало удержания как разрешение выдать результат. Нужное событие завершённого взаимодействия в нашем серверном примере — Triggered.
5. Видимость подсказки и разрешение действия различаются #
MaxActivationDistance=10 задаёт дальность появления подсказки. RequiresLineOfSight=true учитывает ясный путь от камеры к объекту; это не то же измерение, что расстояние от HumanoidRootPart до Attachment. При стене или иной позиции камеры интерфейс может скрыться даже рядом со станцией. Сначала проверяйте одну причину: дистанцию, затем обзор.
Скрытая подсказка не заменяет серверное правило доступа. Свойства интерфейса могут изменяться на клиенте. Этот пример дополнительно проверяет живого персонажа, расстояние и разрешённую станцию, но не реализует серверный запрет взаимодействия через стену. Если такая преграда важна для вашей механики, потребуется отдельная проверка препятствия по выбранным игровым правилам. Не выдавайте RequiresLineOfSight за готовую защиту двери или покупки.
| Параметр | Значение примера | Что проверить |
|---|---|---|
| ActionText / ObjectText | Переключить свет / Учебная лампа | Названы действие и объект; UI_LANGUAGE="ru" |
| KeyboardKeyCode / GamepadKeyCode | E / ButtonX | Подсказка соответствует текущему вводу |
| ClickablePrompt | true | Клик и touch; false не запрещает мобильное нажатие |
| HoldDuration | 0.3 секунды | Короткое/полное удержание и отмена |
| MaxActivationDistance | 10 | Граница появления и отдельная серверная дистанция |
| RequiresLineOfSight | true | Обзор камеры; не серверное разрешение |
| COOLDOWN | 0.5 секунды, общий | Слишком быстрый повтор не переключает лампу |
6. Подключите один серверный обработчик #
Вставьте оригинальный пример ниже в PracticeStationServer. Начальная часть находит четыре объекта, проверяет их типы, выбирает подписи и задаёт параметры. Затем устанавливает выключенное состояние. Не запускайте одновременно второй такой же Script: два обработчика могут переключить свет дважды и создать видимость отсутствующего результата.
Triggered передаёт игрока, завершившего взаимодействие. Сервер находит его персонажа и проверяет условия до изменения лампы. Между проверкой короткого интервала и сменой состояния нет ожидания. Мы не добавляем RemoteEvent, запрос цены или сохранение: всё действие — переключить подготовленную общую лампу. Если вы ещё не различаете место запуска и вид объекта, сначала прочитайте наш материал о расположении Script, LocalScript и ModuleScript.
local Players = game:GetService("Players")
local station = workspace:WaitForChild("PracticeStation")
assert(station:IsA("BasePart") and station.Anchored, "Anchor PracticeStation")
local anchor = station:WaitForChild("PromptAnchor")
assert(anchor:IsA("Attachment"), "PromptAnchor must be an Attachment")
local prompt = anchor:WaitForChild("SwitchPrompt")
assert(prompt:IsA("ProximityPrompt"), "SwitchPrompt must be a ProximityPrompt")
local light = station:WaitForChild("PracticeLight")
assert(light:IsA("PointLight"), "PracticeLight must be a PointLight")
local UI_LANGUAGE = "en" -- Choose one language for this whole test server.
local labels = {
ru = {action = "Переключить свет", object = "Учебная лампа"},
en = {action = "Switch light", object = "Practice lamp"},
de = {action = "Licht umschalten", object = "Übungslampe"},
es = {action = "Cambiar la luz", object = "Lámpara de práctica"},
zh = {action = "切换灯光", object = "练习灯"},
ar = {action = "تبديل الضوء", object = "مصباح التدريب"},
}
local words = assert(labels[UI_LANGUAGE], "Unknown UI_LANGUAGE")
local MAX_DISTANCE = 10
local COOLDOWN = 0.5
prompt.Style = Enum.ProximityPromptStyle.Default
prompt.AutoLocalize = false
prompt.ActionText = words.action
prompt.ObjectText = words.object
prompt.KeyboardKeyCode = Enum.KeyCode.E
prompt.GamepadKeyCode = Enum.KeyCode.ButtonX
prompt.ClickablePrompt = true
prompt.HoldDuration = 0.3
prompt.MaxActivationDistance = MAX_DISTANCE
prompt.RequiresLineOfSight = true
prompt.Enabled = true
local isOn = false
local lastToggle = -COOLDOWN
light.Enabled = false
station.Color = Color3.fromRGB(70, 80, 95)
station:SetAttribute("PracticeLightOn", false)
prompt.Triggered:Connect(function(player)
if player.Parent ~= Players or not prompt.Enabled then return end
if not station:IsDescendantOf(workspace) or not station.Anchored then return end
local character = player.Character
if not character or not character:IsDescendantOf(workspace) then return end
local humanoid = character:FindFirstChildOfClass("Humanoid")
local root = character:FindFirstChild("HumanoidRootPart")
if not humanoid or humanoid.Health <= 0 then return end
if not root or not root:IsA("BasePart") then return end
local distance = (root.Position - anchor.WorldPosition).Magnitude
if distance ~= distance or distance > MAX_DISTANCE then return end
local now = os.clock()
if now - lastToggle < COOLDOWN then return end
lastToggle = now
isOn = not isOn
light.Enabled = isOn
station.Color = isOn and Color3.fromRGB(255, 190, 80) or Color3.fromRGB(70, 80, 95)
station:SetAttribute("PracticeLightOn", isOn)
end)7. Прочитайте проверки до строки переключения #
Обработчик требует игрока в Players, включённую подсказку и закреплённую станцию в Workspace. Затем нужны персонаж в мире, Humanoid с Health>0 и HumanoidRootPart типа BasePart. Расстояние вычисляется на сервере до PromptAnchor.WorldPosition и сравнивается с теми же 10 единицами, которые выбраны для упражнения. Неизвестные или неподходящие состояния просто не меняют свет.
Документация отдельно указывает: Triggered имеет собственную серверную проверку расстояния, тогда как другие события подсказки её не имеют. Наш явный контроль всё равно выражает правило прототипа. HoldDuration не доказывает, что клиент честно удерживал ввод. Это не полноценная защита любой игровой системы: дополнительные права, препятствия и ресурсы зависят от задачи, которой здесь нет.
8. Поймите общее состояние и короткую паузу #
isOn — одно состояние станции на этом сервере. При принятом действии меняются PointLight.Enabled, цвет Part и атрибут PracticeLightOn. Другой клиент, которому доступна эта станция, должен наблюдать то же общее состояние. Это не персональное включение для каждого игрока. В другом сервере или после нового запуска пример начинает с выключенного света, поскольку сохранения нет.
COOLDOWN=0.5 — общая короткая пауза станции, выбранная для демонстрации. Слишком быстрый следующий запрос не переключает свет. Она отличается от времени удержания и не обещает универсального ограничения нагрузки. Во время проверки двух клиентов оставьте паузу между действиями: А включает, Б видит, Б переключает, А видит. Не считайте действие Б доказательством сбоя у А.
9. Когда нужен ProximityPromptService #
Для одной станции достаточно prompt.Triggered. ProximityPromptService позволяет слушать взаимодействия разных подсказок из общего места; у PromptTriggered аргументы начинаются с самой подсказки, затем идёт игрок. При расширении сначала определите, какие станции допускаются, вместо выполнения одной операции для любого найденного Prompt.
Не подключайте обработчик экземпляра и глобальный обработчик к той же лампе без причины: одно действие может обработаться дважды. События показа и скрытия используют при клиентском оформлении, особенно собственном UI. В этом уроке Style=Default уже даёт стандартный интерфейс, поэтому отдельный рисующий LocalScript не требуется. Кастомный стиль лучше разбирать после работающей структуры и понятной серверной операции.
10. Найдите конкретную причину, если ничего не видно #
Если подсказки нет, проверьте дерево, Enabled, расстояние, Style и положение камеры. В перегруженной сцене несколько подсказок могут конкурировать по правилам Exclusivity; наша первая проверка специально содержит одну станцию. Не переключайте все параметры сразу. Сначала подойдите ближе при открытом обзоре, затем меняйте одну настройку и записывайте результат.
Если подсказка видна, но свет не меняется, проверьте Output сервера, тип и RunContext Script, точные имена и принятую паузу. Если цвет меняется, а окружение не освещается, отдельно посмотрите PointLight, Range, Brightness и условия сцены. Отказы в обработчике используют тихий return без печати причины; сопоставьте условия персонажа и станции с кодом. Серверный атрибут помогает отличить результат логики от впечатления о яркости. Таблица сценариев ниже оставляет результаты пустыми до вашей реальной проверки.
| Сценарий | Ожидание для примера | Устройство / версия / результат |
|---|---|---|
| Живой персонаж рядом, полный ввод | Свет, цвет и атрибут меняются один раз | — |
| Короткое удержание и отпускание | В обычном UI завершённое действие не ожидается | — |
| Дальняя или мёртвая фигура; Prompt выключен | Обработчик не меняет состояние | — |
| Быстрый повтор раньше паузы | Состояние не переключается снова | — |
| Два клиента, действия после паузы | Оба наблюдают общую станцию | — |
| Телефон/геймпад и нужный язык | Правильный ввод и читаемые подписи | — |
11. Отделите проверенный текст от проверки движка #
Для этого материала проверены структура статьи и используемые API по официальной справке. Пример прошёл компиляцию Luau и 13 изолированных проверок обработчика с заменителями объектов движка. Код не запускался в Roblox Studio и не проверялся на реальном телефоне или геймпаде. В отдельном учебном проекте выполните первый запуск с игроком, затем испытайте расстояние, отмену удержания, отключение Prompt и быстрое повторение.
Для взаимодействия используйте Test/F5 с персонажем; Run/F8 без персонажа его не заменяет. Для двух клиентов выберите Server & Clients; завершайте испытание через End Session. После этого проверьте нужные устройства, сохранив версию проекта и наблюдения. Эмуляция помогает увидеть компоновку, но не заменяет испытание реального ввода и производительности. Если хотите перенести идею на выдачу питомца или покупку, это будет другая система с правами, согласованной выдачей и отдельными тестами. Сначала добейтесь понятного результата у одной безопасной учебной лампы; такой образец легче обсуждать с другим разработчиком.
Первоисточники
Roblox Creator Hub — Proximity promptsProximityPrompt
ProximityPromptService
Securing the client-server boundary
Studio testing modes