Creating a Modular Prompt System

If you are using Knit then this thread will assume you already know the basics of using Knit, if you don’t know how to use Knit the steps should be mostly similar to either way. I have also added how to create this without Knit

What is a Prompt

If you have played many of the front page games, you will almost always see a similar system in place which allows for a prompt to appear for a player. A prompt system is used for confirming player actions and can help resolve issues where players might have clicked on an action accidentally. A player should almost never have to deal with issues where they accidentally click on a button, and it ends up making a player lose some of their currency, or making them buy something they didn't want. This is why buying items in-game brings up a prompt to confirm your action in Roblox.

RobloxPlayerBeta_jDbiulYP6H

This can also save you from some people expecting refunds after buying an item accidentally

Examples of Prompt Systems in Front Page games

Adopt Me

RobloxPlayerBeta_fQRo0OKyuS

Jailbreak

RobloxPlayerBeta_a1pXgjNbya

MeepCity

RobloxPlayerBeta_bC90QP76Xp

Setting up the Template

Before we get into the scripting required for this system, we need to have a template ready for the prompt. I have created this basic Prompt GUI which I will be using for this tutorial (I know it looks ugly, I am not an artistic person)


RobloxStudioBeta_mBl36fnvTQ
Make sure the background transparency for the Body, Title and Buttons are set to 1 (I kept it at 0.5 to show the size and position they are at

The Template you make does not have to look like this, you could create one that is entirely different. Your Text Labels and Frames don’t need to have the same names either. if you decide to change the names then the only things you will have to change are the names in the code to the name that represents those instances.

After we have created the template, we will need buttons to represent our two choices (usually those choices are accept/deny. Of course the Text for these buttons will be changed when we actually create the prompt!

:tada: Our Template is almost done, now we just have to parent them in the places where we can use them with ease. For my case, I have created a Controller under Replicated Storage and parented the
two buttons and the Prompt GUI separately under the Controller

RobloxStudioBeta_WZR0XOxULK

Creating the Controller

:tada: The template is complete and ready to be scripted!

Before we begin our coding, we should plan out how we want our script to work, this is a great idea for small and large-scale tasks, and in this case it should be relatively easy! here are the things we need the prompt system to accomplish.

  1. Prompt is created through a module using Prompt:Create(parameters)
  2. The parameters indicate most of what will be done to that prompt (such as the title, body, and if we want an accept button, or deny button, or both, and the text that should be in those buttons, and what should happen when we click those buttons)
  3. Setup connection to the buttons created by our parameters (#2)
  4. Whenever the buttons are clicked, we disconnect the events, run our callback, and then destroy the Prompt

Now that we have our Prompt planned out, we can begin to work on it (I can tell some people are saying “Finally!”)

Since we want to use a Knit Controller and our Controller’s purpose is to create Prompts, we might aswell name our controller Prompt Controller. Here’s what this looks like

-- SERVICES --
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

-- VARIABLES --
local Player = Players.LocalPlayer -- For Player.PlayerGui

-- MODULES --
local Knit = require(ReplicatedStorage.Knit)

-- KNIT CONTROLLER --
local PromptController = Knit.CreateController {
    Name = "PromptController";
}


return PromptController

if you are not using Knit, you can get rid of the parts with Knit and set PromptController to table directly

local PromptController = {}

as we stated earlier, we want to create a prompt using Prompt:Create(settings) and we can do that by adding a method to the Controller or if you aren’t using Knit you can just add a method to the PromptController table

function PromptController:Create(settingsTbl) -- The layout for this table will be explained

end

Now we can create a Prompt simply by doing:

local PromptController = Knit.Controllers.PromptController
PromptController:Create(SettingsTbl)

or If you aren’t using Knit you can just do

function PromptController:Create(settingsTbl) -- The layout for this table will be explained

end

PromptController:Create(settingsTbl)

in our planning (#2) we stated that most of our prompt, such as the Text Labels, and Buttons will be controlled by the SettingsTbl provided to the PromptController:Create(SettingsTbl) and because of this we must keep a watch on our layout for this table. For this system it’s really easy, here is a template of our SettingsTbl and how it should look:

{
	Title = "", -- This will be the title of our prompt
	Body = "", -- This will be the body of our prompt
	
	-- V These are optional V --
	
	AcceptButton = { -- This table will allow us to create an AcceptButton
		Text = "", -- This will be the Text of the AcceptButton
		Callback = function() -- This function will run when the player clicks the AcceptButton
			
		end,
	}, 
	DenyButton = { -- This table will allow us to create an DenyButton
		Text = "", -- This will be the Text of the DenyButton
		Callback = function() -- This function will run when the player clicks the DenyButton

		end,
	}, 
}

Now that we have the layout of our SettingsTbl, we can create our prompt based on that. But before we jump to that, we must indicate where our PromptGUI and the AcceptButton and DenyButton are. Remember that we parented them to our current module so we can get them all through using script

-- VARIABLES --
local PromptGUI = script.PromptGUI
local AcceptButton = script.AcceptButton
local DenyButton = script.DenyButton

Now that we have our variables set in place, we can begin on our :Create() method. Since our Title and Body and both mandatory while the AcceptButton and DenyButton are optional, we must check if the Title and Body exist, if not then we will error. To do this we can simply add an assert()

function PromptController:Create(settingsTbl)
	assert(settingsTbl.Title, "Prompt Title not found") -- This will cause an error if the Title is not found 
	assert(settingsTbl.Body, "Prompt Body not found") -- This will cause an error if the Body is not found
    
	local Prompt = PromptGUI:Clone() -- Clone our GUI so we can use it multiple times
	local PromptFrame = Prompt.UI_Prompt
	local ButtonHolder = PromptFrame.Buttons
	
	PromptFrame.Title.Text = settingsTbl.Title -- This will change the Text for the Title
	PromptFrame.Body.Text = settingsTbl.Body -- This will change the Text for the Body

    Prompt.Parent = Player.PlayerGui
end

We have completed our basic prompt and since we want to test this, we can use the :KnitStart() method provided by Knit to test it easily without having to go in a different script.

function PromptController:KnitStart()
	self:Create({
		Title = "TestTitle",
		Body = "TestBody"
	})
end

If you are not using Knit then you can just do

function PromptController:Create(settingsTbl)
	assert(settingsTbl.Title, "Prompt Title not found") -- This will cause an error if the Title is not found 
	assert(settingsTbl.Body, "Prompt Body not found") -- This will cause an error if the Body is not found
    
	local Prompt = PromptGUI:Clone() -- Clone our GUI so we can use it multiple times
	local PromptFrame = Prompt.UI_Prompt
	local ButtonHolder = PromptFrame.Buttons
	
	PromptFrame.Title.Text = settingsTbl.Title -- This will change the Text for the Title
	PromptFrame.Body.Text = settingsTbl.Body -- This will change the Text for the Body

    Prompt.Parent = Player.PlayerGui
end

PromptController:Create({
	Title = "TestTitle",
	Body = "TestBody"
})

You will notice that when you join the game, it should create a prompt and set the Title and Body Text Label’s text property to the ones we provided in the SettingsTbl (in this case “TestTitle” and “TestBody”) But your probably wondering (???) where are the buttons?? well… let’s add them. We have to add them based on our SettingsTbl which should be pretty easy. All we have to do is check if the AcceptButton or DenyButton tables exist in our SettingsTbl

PromptFrame.Title.Text = settingsTbl.Title -- This will change the Text for the Title
PromptFrame.Body.Text = settingsTbl.Body -- This will change the Text for the Body

local AcceptSettings = settingsTbl.AcceptButton
local DenySettings = settingsTbl.DenyButton

if AcceptSettings then
-- This code will run only if settingsTbl.AcceptButton exists
end

if DenySettings then
-- This code will run only if settingsTbl.AcceptButton exists
end

for some micro optimization (because why not) I decided to create a function to create the button we need

local function CreateButton(BtnType, BtnText, Callback)
	local Button = BtnType:Clone() -- Clones the ButtonType passed to it
	Button.Parent = ButtonHolder -- The Button is now in our ButtonFrame
	
	Button:FirstChildWhichIsA("TextLabel").Text = BtnText -- Set the TextLabel's Text Property in our Button
	
	Button.MouseButton1Click:Connect(function() -- Detects when our Button is Clicked
		if Callback then -- Make sure our callback exists, because we might want to create a deny button to cancel the action
			Callback() -- call the callback function
		end
		Prompt:Destroy() -- This will delete our connections set to the buttons as well as clean up the PromptGUI
	end)
end

Now our Buttons can be created with ease, and all we have to do is pass the buttons to the CreateButton function if the button settings exist

local function CreateButton(BtnType, BtnText, Callback)
	local Button = BtnType:Clone()
	Button.Parent = ButtonHolder
	
	Button.Text = BtnText
	
	Button.MouseButton1Click:Connect(function()
		if Callback then
			Callback()
		end
		Prompt:Destroy() -- This will delete our connections set to the buttons as well as clean up the PromptGUI
	end)
end

if AcceptSettings then
	assert(AcceptSettings.Text, "Text for AcceptButton not found") -- Will Error if Text for AcceptButton does not exist
	CreateButton(AcceptButton, AcceptSettings.Text, AcceptSettings.Callback)
end

if DenySettings then
	assert(DenySettings.Text, "Text for DenyButton not found") -- Will Error if Text for DenyButton does not exist
	CreateButton(DenyButton, DenySettings.Text, DenySettings.Callback)
end

Now if we run our code in the same way we did above but pass it these settings instead

function PromptController:KnitStart()
    local settingsTbl = {
		Title = "TestTitle", -- This will set the text for the title
		Body = "TestBody", -- This will set the text for the body
		
		AcceptButton = { -- This will create the AcceptButton
			Text = "AcceptTest", -- This will set the text for the AcceptButton
			Callback = function() -- This will run when we call "AcceptSettings.Callback()"
				print("Accept Button was clicked")
			end,
		},
		
		DenyButton = { -- This will create the DenyButton
			Text = "DenyTest", -- This will set the text for the DenyButton
			Callback = function() -- This will run when we call "DenySettings.Callback()"
				print("Deny Button was clicked")
			end,
		}
	}
	self:Create(settingsTbl)
end

or if you are not using Knit, you can do pass the settingsTbl above to PromptController:Create(settingsTbl)

Now if we run our game and test out our Prompt System you will see that it works exactly as we want

Completed

:tada: The Prompt System is complete and it works the way we envisioned it to work! Of course the rest of the System is mostly personal preference, as that’s where most of the creativity comes in!
I have made this uncopylocked and you can check it out here

Whole Script w/ Knit
-- SERVICES --
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

-- VARIABLES --
local PromptGUI = script.PromptGUI
local AcceptButton = script.AcceptButton
local DenyButton = script.DenyButton 

local Player = Players.LocalPlayer

-- MODULES --
local Knit = require(ReplicatedStorage.Knit)

-- KNIT CONTROLLER --
local PromptController = Knit.CreateController {
	Name = "PromptController";
}

--[[
local SettingsTbl = {
	Title = "", -- This will be the title of our prompt
	Body = "", -- This will be the body of our prompt
	
	-- V These are optional V --
	
	AcceptButton = { -- This table will allow us to create an AcceptButton
		Text = "", -- This will be the Text of the AcceptButton
		Callback = function() -- This function will run when the player clicks the AcceptButton
			
		end,
	}, 
	DenyButton = { -- This table will allow us to create an DenyButton
		Text = "", -- This will be the Text of the DenyButton
		Callback = function() -- This function will run when the player clicks the DenyButton

		end,
	}, 
}
]]--


function PromptController:Create(settingsTbl)	
	assert(settingsTbl.Title, "Prompt Title not found") -- This will cause an error if the Title is not found 
	assert(settingsTbl.Body, "Prompt Body not found") -- This will cause an error if the Body is not found
	
	local Prompt = PromptGUI:Clone() -- Clone our GUI so we can use it multiple times
	local PromptFrame = Prompt.UI_Prompt
	local ButtonHolder = PromptFrame.Buttons
	
	PromptFrame.Title.Text = settingsTbl.Title -- This will change the Text for the Title
	PromptFrame.Body.Text = settingsTbl.Body -- This will change the Text for the Body
	
	local AcceptSettings = settingsTbl.AcceptButton
	local DenySettings = settingsTbl.DenyButton
	
	local function CreateButton(BtnType, BtnText, Callback)
		local Button = BtnType:Clone()
		Button.Parent = ButtonHolder
		
		Button.Text = BtnText
		
		Button.MouseButton1Click:Connect(function()
			if Callback then
				Callback()
			end
			Prompt:Destroy() -- This will delete our connections set to the buttons as well as clean up the PromptGUI
		end)
	end

	if AcceptSettings then
		assert(AcceptSettings.Text, "Text for AcceptButton not found")
		CreateButton(AcceptButton, AcceptSettings.Text, AcceptSettings.Callback)
	end

	if DenySettings then
		assert(DenySettings.Text, "Text for DenyButton not found")
		CreateButton(DenyButton, DenySettings.Text, DenySettings.Callback)
	end

	Prompt.Parent = Player.PlayerGui
end


function PromptController:KnitStart()
	self:Create({
		Title = "Knit Test",
		Body = "This Prompt is made via Knit",
		
		AcceptButton = {
			Text = "AcceptTest",
			Callback = function()
				print("Accept Button was clicked")
			end,
		},
		
		DenyButton = {
			Text = "DenyTest",
			Callback = function()
				print("Deny Button was clicked")
			end,
		}
	})
end

return PromptController
Whole Script w/o Knit
-- SERVICES --
local Players = game:GetService("Players")

-- VARIABLES --
local PromptGUI = script.PromptGUI
local AcceptButton = script.AcceptButton
local DenyButton = script.DenyButton 

local Player = Players.LocalPlayer

-- CONTROLLER --
local PromptController = {}

--[[
local SettingsTbl = {
	Title = "", -- This will be the title of our prompt
	Body = "", -- This will be the body of our prompt
	
	-- V These are optional V --
	
	AcceptButton = { -- This table will allow us to create an AcceptButton
		Text = "", -- This will be the Text of the AcceptButton
		Callback = function() -- This function will run when the player clicks the AcceptButton
			
		end,
	}, 
	DenyButton = { -- This table will allow us to create an DenyButton
		Text = "", -- This will be the Text of the DenyButton
		Callback = function() -- This function will run when the player clicks the DenyButton

		end,
	}, 
}
]]--


function PromptController:Create(settingsTbl)	
	assert(settingsTbl.Title, "Prompt Title not found") -- This will cause an error if the Title is not found 
	assert(settingsTbl.Body, "Prompt Body not found") -- This will cause an error if the Body is not found
	
	local Prompt = PromptGUI:Clone() -- Clone our GUI so we can use it multiple times
	local PromptFrame = Prompt.UI_Prompt
	local ButtonHolder = PromptFrame.Buttons
	
	PromptFrame.Title.Text = settingsTbl.Title -- This will change the Text for the Title
	PromptFrame.Body.Text = settingsTbl.Body -- This will change the Text for the Body
	
	local AcceptSettings = settingsTbl.AcceptButton
	local DenySettings = settingsTbl.DenyButton
	
	local function CreateButton(BtnType, BtnText, Callback)
		local Button = BtnType:Clone()
		Button.Parent = ButtonHolder
		
		Button.Text = BtnText
		
		Button.MouseButton1Click:Connect(function()
			if Callback then
				Callback()
			end
			Prompt:Destroy() -- This will delete our connections set to the buttons as well as clean up the PromptGUI
		end)
	end

	if AcceptSettings then
		assert(AcceptSettings.Text, "Text for AcceptButton not found")
		CreateButton(AcceptButton, AcceptSettings.Text, AcceptSettings.Callback)
	end

	if DenySettings then
		assert(DenySettings.Text, "Text for DenyButton not found")
		CreateButton(DenyButton, DenySettings.Text, DenySettings.Callback)
	end

	Prompt.Parent = Player.PlayerGui
end

PromptController:Create({
	Title = "Test",
	Body = "This Prompt is not made via Knit",

	AcceptButton = {
		Text = "AcceptTest",
		Callback = function()
			print("Accept Button was clicked")
		end,
	},

	DenyButton = {
		Text = "DenyTest",
		Callback = function()
			print("Deny Button was clicked")
		end,
	}
})


return PromptController
Did you find this helpful?
  • Yes
  • No

0 voters

9 Likes

Points for the effort taken to do this. Though if I was you, I’d focus on making posts without needed dependencies such as Knit.

2 Likes

I completely agree!. After I finished writing most of it I suddenly realized that not a lot of people use Knit, but I was already almost done so I thought might aswell finish it.

1 Like

I still want to thank you for this tutorial!
It gave me better understanding on how to use Knit.
There is not a lot of code examples that have practical use.
Also thank you for giving comprehension between Knit code and basic code

1 Like