These will be very useful. Thank you!
Yeah I did have this issue when i was using it, maybe you should add like 1 second of delay before reenabling the second Prompt.
Yes I know that, but the game was still playable, ofcourse it no longer worked like it should have but was playable, what I mean by playable is that you can load into the game without errors
Is it possible to have a customize feature? So we can customize the ui? (Different colors, etc)
It is because you’re changing it on the client side and not the server side.
Is anyone noticing some odd behavior from Custom ProximityPrompts, specially on mobile devices? The output isn’t really giving me a ton of meaningful information as to what is going wrong here. Mobile devices are having issues where this table being returned is actually returning nil.
local datastuff = {
Cleanup = function()
PromptUI.Parent = nil
end;
Toggle = function(Visible)
PromptUI.Enabled = Visible
end;
}
warn(datastuff)
return datastuff
This is really odd because my code works perfectly fine on PC, and the warn statement above will print a memory address for the table. The only outside library I am using here is Knit, which just functions as a framework for my codebase. You can view the full client-side code responsible for handling interactions here:
-- InteractionController
-- Konstantine Papa
-- January 27, 2021
local ProximityPromptService = game:GetService("ProximityPromptService")
local UserInputService = game:GetService("UserInputService")
local TextService = game:GetService("TextService")
local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local Knit = require(game:GetService("ReplicatedStorage").Modules.Knit)
local InteractionService = Knit.GetService("InteractionService")
local InteractionController = Knit.CreateController { Name = "InteractionController" }
local Player = Players.LocalPlayer
local PlayerGui = Player:WaitForChild("PlayerGui")
local GamepadButtonImage = {
[Enum.KeyCode.ButtonX] = "rbxasset://textures/ui/Controls/xboxX.png";
[Enum.KeyCode.ButtonY] = "rbxasset://textures/ui/Controls/xboxY.png";
[Enum.KeyCode.ButtonA] = "rbxasset://textures/ui/Controls/xboxA.png";
[Enum.KeyCode.ButtonB] = "rbxasset://textures/ui/Controls/xboxB.png";
[Enum.KeyCode.DPadLeft] = "rbxasset://textures/ui/Controls/dpadLeft.png";
[Enum.KeyCode.DPadRight] = "rbxasset://textures/ui/Controls/dpadRight.png";
[Enum.KeyCode.DPadUp] = "rbxasset://textures/ui/Controls/dpadUp.png";
[Enum.KeyCode.DPadDown] = "rbxasset://textures/ui/Controls/dpadDown.png";
[Enum.KeyCode.ButtonSelect] = "rbxasset://textures/ui/Controls/xboxmenu.png";
[Enum.KeyCode.ButtonL1] = "rbxasset://textures/ui/Controls/xboxLS.png";
[Enum.KeyCode.ButtonR1] = "rbxasset://textures/ui/Controls/xboxRS.png";
}
local KeyboardButtonImage = {
[Enum.KeyCode.Backspace] = "rbxasset://textures/ui/Controls/backspace.png";
[Enum.KeyCode.Return] = "rbxasset://textures/ui/Controls/return.png";
[Enum.KeyCode.LeftShift] = "rbxasset://textures/ui/Controls/shift.png";
[Enum.KeyCode.RightShift] = "rbxasset://textures/ui/Controls/shift.png";
[Enum.KeyCode.Tab] = "rbxasset://textures/ui/Controls/tab.png";
}
local KeyboardButtonIconMapping = {
["'"] = "rbxasset://textures/ui/Controls/apostrophe.png";
[","] = "rbxasset://textures/ui/Controls/comma.png";
["`"] = "rbxasset://textures/ui/Controls/graveaccent.png";
["."] = "rbxasset://textures/ui/Controls/period.png";
[" "] = "rbxasset://textures/ui/Controls/spacebar.png";
}
local KeyCodeToTextMapping = {
[Enum.KeyCode.LeftControl] = "Ctrl";
[Enum.KeyCode.RightControl] = "Ctrl";
[Enum.KeyCode.LeftAlt] = "Alt";
[Enum.KeyCode.RightAlt] = "Alt";
[Enum.KeyCode.F1] = "F1";
[Enum.KeyCode.F2] = "F2";
[Enum.KeyCode.F3] = "F3";
[Enum.KeyCode.F4] = "F4";
[Enum.KeyCode.F5] = "F5";
[Enum.KeyCode.F6] = "F6";
[Enum.KeyCode.F7] = "F7";
[Enum.KeyCode.F8] = "F8";
[Enum.KeyCode.F9] = "F9";
[Enum.KeyCode.F10] = "F10";
[Enum.KeyCode.F11] = "F11";
[Enum.KeyCode.F12] = "F12";
}
function InteractionController:KnitStart()
local function GetScreenGui()
local ScreenGui = PlayerGui:FindFirstChild("Interactions")
if (ScreenGui == nil) then
ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = "Interactions"
ScreenGui.ResetOnSpawn = false
ScreenGui.Parent = PlayerGui
end
return ScreenGui
end
local function CreatePrompt(Prompt, InputType, Gui)
local PromptUI = Instance.new("BillboardGui")
PromptUI.Name = "Prompt"
PromptUI.LightInfluence = 0
PromptUI.AlwaysOnTop = true
PromptUI.Size = UDim2.new(0, 200, 0, 80)
local Frame = Instance.new("Frame")
Frame.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
Frame.AnchorPoint = Vector2.new(0.5, 0.5)
Frame.Position = UDim2.new(0.5, 0, 0.5, 0)
Frame.Size = UDim2.new(1, 0, 1, 0)
Frame.Parent = PromptUI
local UIAspectRatioConstraint = Instance.new("UIAspectRatioConstraint")
UIAspectRatioConstraint.Parent = Frame
local UICorner = Instance.new("UICorner")
UICorner.CornerRadius = UDim.new(0.15, 0)
UICorner.Parent = Frame
local KeyLabel = Instance.new("TextLabel")
KeyLabel.BackgroundTransparency = 1
KeyLabel.Name = "KeyLabel"
KeyLabel.Position = UDim2.new(0, 0, 0.25, 0)
KeyLabel.Size = UDim2.new(1, 0, 0.7, 0)
KeyLabel.Font = Enum.Font.SourceSansBold
KeyLabel.TextColor3 = Color3.fromRGB(121, 201, 173)
KeyLabel.TextScaled = true
KeyLabel.Parent = Frame
local Header = Instance.new("Frame")
Header.BackgroundColor3 = Color3.fromRGB(121, 201, 173)
Header.Name = "Header"
Header.Size = UDim2.new(1, 0, 0.3, 0)
Header.Parent = Frame
local HeaderUICorner = Instance.new("UICorner")
HeaderUICorner.CornerRadius = UDim.new(0.45, 0)
HeaderUICorner.Parent = Header
local HeaderFrame = Instance.new("Frame")
HeaderFrame.AnchorPoint = Vector2.new(0, 1)
HeaderFrame.BorderSizePixel = 0
HeaderFrame.BackgroundColor3 = Color3.fromRGB(121, 201, 173)
HeaderFrame.Position = UDim2.new(0, 0, 1, 0)
HeaderFrame.Size = UDim2.new(1, 0, 0.5, 0)
HeaderFrame.Parent = Header
local TitleLabel = Instance.new("TextLabel")
TitleLabel.BackgroundTransparency = 1
TitleLabel.Name = "Title"
TitleLabel.Size = UDim2.new(1, 0, 1, 0)
TitleLabel.Font = Enum.Font.SourceSansBold
TitleLabel.Text = Prompt.ActionText
TitleLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
TitleLabel.TextScaled = true
TitleLabel.Parent = Header
if (InputType == Enum.ProximityPromptInputType.Gamepad) then
if GamepadButtonImage[Prompt.GamepadKeyCode] then
KeyLabel:Destroy()
KeyLabel = Instance.new("ImageLabel")
KeyLabel.BackgroundTransparency = 1
KeyLabel.Name = "KeyLabel"
KeyLabel.Position = UDim2.new(0, 0, 0.25, 0)
KeyLabel.Size = UDim2.new(1, 0, 0.7, 0)
KeyLabel.ImageTransparency = 1
KeyLabel.Image = GamepadButtonImage[Prompt.GamepadKeyCode]
KeyLabel.Parent = Frame
end
elseif (InputType == Enum.ProximityPromptInputType.Touch) then
KeyLabel:Destroy()
KeyLabel = Instance.new("ImageLabel")
KeyLabel.BackgroundTransparency = 1
KeyLabel.Name = "KeyLabel"
KeyLabel.Position = UDim2.new(0, 0, 0.25, 0)
KeyLabel.Size = UDim2.new(1, 0, 0.7, 0)
KeyLabel.ImageTransparency = 1
KeyLabel.Image = "rbxasset://textures/ui/Controls/TouchTapIcon.png"
KeyLabel.Parent = Frame
else
local ButtonTextString = UserInputService:GetStringForKeyCode(Prompt.KeyboardKeyCode)
local ButtonTextImage = KeyboardButtonImage[Prompt.KeyboardKeyCode]
if (ButtonTextImage == nil) then
ButtonTextImage = KeyboardButtonIconMapping[ButtonTextString]
end
if (ButtonTextImage == nil) then
local KeyCodeMappedText = KeyCodeToTextMapping[Prompt.KeyboardKeyCode]
if (KeyCodeMappedText) then
ButtonTextString = KeyCodeMappedText
end
end
if (ButtonTextImage) then
KeyLabel:Destroy()
KeyLabel = Instance.new("ImageLabel")
KeyLabel.BackgroundTransparency = 1
KeyLabel.Name = "KeyLabel"
KeyLabel.Position = UDim2.new(0, 0, 0.25, 0)
KeyLabel.Size = UDim2.new(1, 0, 0.7, 0)
KeyLabel.ImageTransparency = 1
KeyLabel.Image = ButtonTextImage
KeyLabel.Parent = Frame
elseif (ButtonTextString ~= nil and ButtonTextString ~= " ") then
KeyLabel.Text = ButtonTextString
else
error("ProximityPrompt " .. Prompt.Name .. " has an unsupported keycode for rendering UI; " .. tostring(Prompt.KeyboardKeyCode))
end
if (InputType == Enum.ProximityPromptInputType.Touch or Prompt.ClickablePrompt) then
local Button = Instance.new("TextButton")
Button.BackgroundTransparency = 1
Button.TextTransparency = 1
Button.Size = UDim2.fromScale(1, 1)
Button.Parent = PromptUI
local ButtonDown = false
Button.InputBegan:Connect(function(Input)
if ((Input.UserInputType == Enum.UserInputType.Touch) or Input.UserInputType == Enum.UserInputType.MouseButton1 and Input.UserInputState ~= Enum.UserInputState.Change) then
Prompt:InputHoldBegin()
ButtonDown = true
end
end)
Button.InputEnded:Connect(function(Input)
if (Input.UserInputType == Enum.UserInputType.Touch or Input.UserInputType == Enum.UserInputType.MouseButton1) then
if (ButtonDown) then
ButtonDown = false
Prompt:InputHoldEnd()
end
end
end)
PromptUI.Active = true
end
PromptUI.Adornee = Prompt.Parent
PromptUI.Parent = Gui
local datastuff = {
Cleanup = function()
PromptUI.Parent = nil
end;
Toggle = function(Visible)
PromptUI.Enabled = Visible
end;
}
warn(datastuff)
return datastuff
end
end
do
local function OnTriggered(Prompt, Cache)
if (Prompt.ActionText == "Sit") then
Cache.Toggle(false)
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")
Humanoid:GetPropertyChangedSignal("Sit"):Connect(function()
if (not Humanoid.Sit) then
Cache.Toggle(true)
end
end)
end
end
ProximityPromptService.PromptShown:Connect(function(Prompt, InputType)
if Prompt.Style == Enum.ProximityPromptStyle.Default then
return
end
local Cache = CreatePrompt(Prompt, InputType, GetScreenGui())
Prompt.Triggered:Connect(function()
OnTriggered(Prompt, Cache)
end)
Prompt.PromptHidden:Wait()
Cache.Cleanup()
end)
end
end
function InteractionController:KnitInit()
end
return InteractionController
I’ve gone as far as to place print statements everywhere and it has all come down to that datastuff
table returning nil on mobile. The fact this problem is only occuring on mobile makes me think it’s an issue with ProximityPrompt.
If I can’t find a fix for this issue I think my best bet would be to continue with coding my own interaction system without the use of ProximityPrompt. It’s already proved to be pretty problematic for me and it’s ridiculous how much work needs to go into it to provide some basic functionality.
I’d have to say, Proximity Prompts are one of the best things made on the platform. Such a simple tool can be taken so far with the style feature.
Users might think “I can only change the design of it” Well, thats where your wrong.
I’m currently working on a system that allowed me to make Interactions that look like a entire custom system. when its literally just proximity prompts.
Its still in testing/development and I plan on adding a few more things before its finished. I will not be open sourcing it or giving it out for sale but I want to let you guys see what you can really do if you put your mind to it with these prompts.
You’re absolutely correct! I use an invisible custom ProximityPrompt to trigger open and close events on doors in my game:
I do find that if the ProximityPrompt is out of view of the camera, it fires the PromptHidden event, which isn’t ideal for doors, as with certain doors they will close while you’re still walking through them if the door is large enough.
I would appreciate if Roblox could add a property to disable this behaviour or a separate event for approached/departed (which would be great for this sort of use-case, where no interaction, apart from pure proximity, is required).
If I understand the code correctly, it looks like you’re only returning datastuff
inside the else
block of this conditional:
elseif (InputType == Enum.ProximityPromptInputType.Touch) then
This might make it not function when on mobile. Possibly try moving the datastuff
code down one indention level.
I like this prompt, I will use it forever!
ProximityPromptService.PromptTriggered
ProximityPromptService.PromptButtonHoldBegan
ProximityPromptService.PromptButtonHoldEnded
Should all have Player as the first argument.
This in order to stay consistent with RemoteEvents, RemoteFunctions and ProximityPrompts: Triggered, PromptButtonHoldBegan and PromptButtonHoldEnded events.
I disagree, I think in the context of those events being Service-sided, the absolute most important argument passed there is the prompt instance. Without the prompt instance, those events are basically useless, meaning you will be using that first argument 100% of the time. By contrast, there’s plenty of uses for those events that might not require knowing which player fired the prompt.
Interesting argument. I think you might have changed my mind
New suggestion!
ProximityPromt should have a Cooldown property!
The Icon will be locally disabled for “Cooldown” amount of seconds after being triggered.
This helps prevent spam and also makes it look better when the action that ProximityPrompt triggers isn’t immidiate (such as teleporting done by server scripts etc).
You can ofcourse custom code this but it’s a frequently occuring scenario so I think it should be a property of it’s own.
Can confirm that this is still a bug and it’s quite frustrating. It’s not that you don’t get the radial effect at all, it’s that the radial progress is the same rate.
This happens even on server-side changes.
I have a problem with Proximity Prompt. I cant use multiple Proximity Prompts in one same model. If i could, it will be very usefull for vehicles.
Try making the lever prompt disabled until the door prompt is activated. For closing, you might want to weld another prompt onto the wall behind it (with an invisible block). Hope this helps!
There could be a visible property to make it invisible
There’s a glitch where when u make it anchored,the GUI will disappear.
Hey @Cypacic, May you provide further information on this?
It may not be a bug/glitch itself but more of something on your end.
nvm just fixed this by making RequiresLineOfSight
false