Proximity Prompt Release

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.

1 Like

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

2 Likes

Is it possible to have a customize feature? So we can customize the ui? (Different colors, etc)

1 Like

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.

1 Like

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.

https://gyazo.com/229b49596ede825148c29c907329266b

2 Likes

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).

3 Likes

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.

2 Likes

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.

1 Like

Interesting argument. I think you might have changed my mind :stuck_out_tongue:

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.

6 Likes

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.

1 Like

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.

1 Like

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