DialogueKit
ReleasedDialogueKit is a free drop-in Roblox NPC dialogue system built around data instead of per-NPC scripts. Add a tagged NPC, give it one DialogueData module, choose Classic or Reimagined, and the shared client/server system handles the rest.
Two dialogue modes
Classic uses the reusable dGui screen template, locks the player in place, focuses a configurable camera on the NPC, and fades the local character to keep the dialogue readable.
Reimagined has no camera. It creates client-only world UI with a BillboardGui above the NPC and an AnswerPart for local answer buttons, so players do not fight over shared replicated dialogue controls.
1return {
2 mode = "Classic",
3 options = {
4 distance = 2,
5 FOV = 90,
6 },
7 dialogue = {
8 start = {
9 speed = 0.03,
10 lines = {
11 "Hello, {player}.",
12 "This is a Classic dialogue.",
13 },
14 answers = {
15 { text = "Continue", next = "more" },
16 { text = "Goodbye" },
17 },
18 },
19 },
20}Drop-in NPC setup
NPCs are discovered through CollectionService. The generic client script watches for the DialogueNPC tag, requires the model's DialogueData, registers it with DialogueSystem, waits for the server-created prompt, and starts dialogue when the prompt is triggered.
1local function setupNpc(model)
2 local data = require(model:WaitForChild("DialogueData"))
3 local mode = data.mode or "Classic"
4 local options = data.options or {}
5
6 DialogueSystem.Dialogues[model.Name] = data.dialogue
7 DialogueSystem:SetMode(mode, options, model.Name)
8
9 local torso = model:FindFirstChild("Torso")
10 or model:FindFirstChild("HumanoidRootPart")
11 or model.PrimaryPart
12
13 local prompt = torso and torso:WaitForChild("ProximityPrompt", 5)
14 if prompt then
15 prompt.Triggered:Connect(function()
16 DialogueSystem:StartDialogue(model.Name)
17 end)
18 end
19endData-driven dialogue
Each node can define typed lines, answer choices, follow-up nodes, and its own typewriter speed. Lower speed values type faster; higher values type slower. The {player} token is replaced with the local player's display name.
1return {
2 mode = "Reimagined",
3 options = {},
4 dialogue = {
5 start = {
6 speed = 0.03,
7 lines = {
8 "Hi, I am using Reimagined mode.",
9 "My text appears above my head.",
10 },
11 answers = {
12 { text = "Show me more", next = "more" },
13 { text = "Goodbye" },
14 },
15 },
16
17 more = {
18 speed = 0.03,
19 lines = {
20 "Answer buttons appear in the world for this player only.",
21 },
22 answers = {
23 { text = "Back", next = "start" },
24 { text = "Goodbye" },
25 },
26 },
27 },
28}Server-approved actions
Rewards and teleports stay server-side. The client only sends the NPC name, current node, and answer index. The server reloads the NPC's DialogueData, validates that answer, then runs a registered action handler if the answer has an action field.
1answers = {
2 {
3 text = "Give me 100 Coins",
4 action = "GiveCoins",
5 amount = 100,
6 next = "coins",
7 },
8 {
9 text = "Teleport me to Town",
10 action = "TeleportToPad",
11 target = "TownTeleport",
12 next = "teleport",
13 },
14}1local actionHandlers = {
2 GiveCoins = function(player, answer)
3 local amount = tonumber(answer.amount) or 0
4 local coins = player:FindFirstChild("leaderstats")
5 and player.leaderstats:FindFirstChild("Coins")
6
7 if coins then
8 coins.Value += amount
9 end
10 end,
11
12 TeleportToPad = function(player, answer)
13 local pads = workspace:FindFirstChild("TeleportPads")
14 local pad = pads and pads:FindFirstChild(answer.target)
15 local root = player.Character
16 and player.Character:FindFirstChild("HumanoidRootPart")
17
18 if pad and root then
19 root.CFrame = pad.CFrame + Vector3.new(0, 3, 0)
20 end
21 end,
22}Adding an NPC
Put the model under Workspace.DialogueFolder, tag it DialogueNPC, add one DialogueData ModuleScript, and press Play. No per-NPC Script or LocalScript is needed.