开发 / ROBLOX
Roblox ProximityPrompt:支持 E、手柄和触控的练习站
在独立项目中创建固定灯、Attachment 和 ProximityPrompt,学习提示文字、显示条件、服务器检查与尚未填写结果的设备测试计划。
一个练习站,一个清楚的动作 #
想象一个独立原型:玩家走到小灯旁,切换灯光。不必寻找看不见的按钮,而是看见对象名称、动作和合适的输入提示。我们使用标准 ProximityPrompt 与普通服务器 Script。示例没有奖励、货币或购买。
这是单独练习,不是修改 Paws and Finds、Cloud Nests 或作者其他游戏。下面的原创代码针对指定结构,静态检查并不等于在 Studio 引擎里运行。先保存独立学习项目,按示意图准确命名,再写出预期:改变共享灯光与练习站颜色,而不是个人库存。范围小,才能清楚判断一次交互究竟改变了什么。
1. 在 Explorer 建立准确层级 #
在 Workspace 新建名为 PracticeStation 的 Part。它里面放名为 PromptAnchor 的 Attachment,以及名为 PracticeLight 的 PointLight。再在 PromptAnchor 里面放名为 SwitchPrompt 的 ProximityPrompt。这是两条分支:光源属于 Part,提示属于里面的 Attachment。不要把提示误放到 Attachment 旁边。
在 ServerScriptService 创建普通 Script,命名 PracticeStationServer,启用并设置 RunContext=Server,不要换成 LocalScript。代码中的名称必须完全匹配。WaitForChild 等待指定名称,并不会生成缺失对象。执行一直等待时,先对照 Explorer 与示意图。SwitchPrompt 在错误分支是层级问题,扩大距离并不能修复它。
2. 固定灯并设置提示位置 #
练习中给 PracticeStation 设置 Anchored=true、Size=(4,2,4)、Position=(0,3,0)。这些是我们的教学数值,不是引擎要求。把 SpawnLocation 放在附近、Part 外面,角色才方便靠近。PromptAnchor.Position=(0,2,0) 是相对练习站的本地位置,不是独立世界坐标。
PracticeLight 可以从 Brightness=2、Range=12 开始。脚本会先关闭灯。为了这次检查,不需要复杂模型、移动连接或已有奖励系统。固定 Part 提供稳定参照,服务器随后量到 anchor.WorldPosition 的距离。移动 Attachment 会改变交互点,因此重新检查边界,不要继续依赖旧位置看到的结果。
3. 用自己的语言写对象与动作 #
ObjectText 回答“这是什么”,ActionText 回答“我要做什么”。中文示例选择“练习灯”和“切换灯光”。代码包含 ru/en/de/es/zh/ar 六组文字。在 UI_LANGUAGE 中选择本次练习场景的语言,例如 "zh"。Explorer 的 PracticeStation、SwitchPrompt 名称不用翻译。
这会为整个测试服务器配置一种语言,不会自动使用每位玩家的语言。AutoLocalize=false 保留已选文字,不做自动替换。正式多语言游戏还需要独立的本地化设计,并检查较长文字。不要把 E 写进 ActionText;描述动作即可,标准提示会根据当前控制方式显示输入。这个字典是练习配置,不是完成的逐玩家语言系统。
4. 区分键盘、手柄与屏幕输入 #
示例设置 KeyboardKeyCode=E、GamepadKeyCode=ButtonX、ClickablePrompt=true。不能保证所有人都会看到 E,当前输入类型会影响标准界面。手机或平板可以直接在屏幕交互;即使 ClickablePrompt=false,也不会禁止移动端点击。因此该属性不是关闭触控访问的总开关。
HoldDuration=0.3 要求普通界面中的短暂按住;设为0则立即触发。0.3 是教学选择,并非测得的最佳值。尝试短按、完整按住及松开。开始按住并不等于可以执行结果。本服务器示例使用完成交互的 Triggered,而不是开始按住的事件。先理解两者,再考虑更复杂的动作。
5. 提示可见与操作许可不是一回事 #
MaxActivationDistance=10 控制提示出现范围。RequiresLineOfSight=true 考虑摄像机到对象的通路,这与 HumanoidRootPart 到 Attachment 的距离不同。墙体或摄像机位置可能让靠近练习站的玩家仍看不到提示。一次检查一个原因:先距离,再视线。
隐藏提示不能代替服务器访问规则。客户端可改变界面属性。示例另外检查角色存活、距离和指定练习站,但并未实现服务器侧禁止隔墙交互。如果障碍物对玩法重要,要按自己的规则增加独立障碍检查。不要把 RequiresLineOfSight 当成门或购买的完整保护。此处的目标只是小灯,不能因此推断所有其他操作都已安全。
| 设置 | 示例值 | 检查内容 |
|---|---|---|
| ActionText / ObjectText | 切换灯光 / 练习灯 | 动作与对象明确;UI_LANGUAGE="zh" |
| KeyboardKeyCode / GamepadKeyCode | E / ButtonX | 提示与当前输入相符 |
| ClickablePrompt | true | 点击与触控;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。还需要世界中的角色、Health>0 的 Humanoid,以及 BasePart 类型的 HumanoidRootPart。服务器计算到 PromptAnchor.WorldPosition 的距离,并与同样的10个练习单位比较。缺少或不符合条件的状态不改变灯。
文档特别说明 Triggered 自带服务器距离检查,而其他提示事件没有。这里的显式检查仍用来表达原型规则。HoldDuration 不能证明客户端诚实地按住了输入。这并非任何玩法的完整保护:额外权限、障碍和资源条件取决于操作,而当前灯没有奖励资格要求。不要把小示例的检查范围扩大解释。
8. 理解共享状态与短暂停顿 #
isOn 是这个服务器里练习站的一个状态。接受操作后改变 PointLight.Enabled、Part 颜色及 PracticeLightOn 属性。另一个能访问这座练习站的客户端应观察相同共享状态,不是每位玩家分别拥有自己的灯开关。另一个服务器或新启动会从关闭开始,因为示例不保存。
COOLDOWN=0.5 是为演示选择的全站短暂停顿。下一请求太快就不切换。它与按住时长不同,也不是通用负载限制保证。两个客户端检查时留出间隔:A打开,B观察,B切换,A观察。B被接受的操作不是A状态故障的证明。先写清共享规则,再解释多人看到的变化。
9. 什么时候使用 ProximityPromptService #
一个站点使用 prompt.Triggered 就足够。ProximityPromptService 可从同一位置监听不同提示;PromptTriggered 先收到提示对象,再收到玩家。扩展时先定义允许哪些站点,而不是对世界里任意 Prompt 执行同一个操作。
不要无理由为同一灯同时连接实例处理器和全局处理器,一个动作可能被处理两次。显示与隐藏事件用于客户端表现,尤其是自定义 UI。本课 Style=Default 已提供标准界面,无需另外绘制的 LocalScript。先让结构与服务器操作工作,再研究定制样式。集中管理很方便,但不能取消每种操作需要的验证,也不会自动把共享灯改为个人结果。
10. 没有提示时寻找具体原因 #
提示缺失时检查层级、Enabled、距离、Style 和摄像机。复杂场景的多个提示可能按 Exclusivity 竞争;初次练习特意只放一座站点。不要同时改变全部属性。先在视线通畅时靠近,再改一个设置并记录结果。
提示出现而灯不变时,检查服务器 Output、Script 类型、RunContext、准确名称及暂停间隔。如果颜色变了但周围不亮,单独查看 PointLight、Range、Brightness 与场景。拒绝分支使用不打印原因的 return;请对照代码检查角色和练习站条件。服务器属性能帮助区分逻辑状态和对亮度的印象。下面场景表的结果格保持空白,直到实际检查。预期不是观察,复制的示例也不是设备测试报告。
| 场景 | 示例预期 | 设备 / 版本 / 结果 |
|---|---|---|
| 存活角色靠近,完整输入 | 灯、颜色、属性改变一次 | — |
| 短暂按住再松开 | 普通界面不预期完成操作 | — |
| 角色远离/死亡,或提示关闭 | 处理器不改状态 | — |
| 暂停结束前重复 | 不再次切换 | — |
| 两个客户端,间隔后操作 | 都观察共享练习站 | — |
| 手机/手柄与选择的语言 | 输入合适,文字可读 | — |
11. 区分材料检查与引擎测试 #
本课检查了文章结构及所用 API 与官方参考的一致性。示例通过 Luau 编译器和13项使用替代对象的独立处理器检查。 代码没有在 Roblox Studio 或真实手机、手柄上运行。先在独立练习项目启动一个玩家,再检查距离、取消按住、关闭提示与快速重复。
交互测试使用带角色的 Test/F5,不能用无角色的 Run/F8 代替。两个客户端选择 Server & Clients,结束时使用 End Session。然后检查所需设备,保留项目版本和观察记录。模拟设备有助于看布局,不能代替真实输入与性能测试。若要改成发宠物或购买,那是另一套系统,需要权限、一致的发放和独立测试。先在一盏简单练习灯上得到清楚结果。这样的具体样例更容易交给其他开发者讨论,也更容易确定哪些地方已经检查,哪些仍需验证。
原始资料
Roblox Creator Hub — Proximity promptsProximityPrompt
ProximityPromptService
Securing the client-server boundary
Studio testing modes