How do I create a talking NPC dialogue system?

You can try doing the typewriter effects. You can see this video on a more detailed explanation https://www.youtube.com/watch?v=DeMLkCnW6oM&t=316s

Typewriting effects, GUI, and lots of RemoteEvents. If you wanna add choices you’re gonna need a ton of checks and dialogues.

Hello!

I can help with this! First, create a part to use as a trigger to detect when the player is close enough. Then create a GUI and make a typewriter effect sort of like this:

local TextLabel = script.Parent

local function AutoText(object, text)
	for i = 1, #text, 1 do
		object.Text = string.sub(text, 1, i)
		task.wait(0.05)
	end
end

AutoText(TextLabel, "your text here") -- The second parameter is the text you want to display.

DM me for further details! Hopes this helps! :slight_smile:

4 Likes

Alright, so, this is what I have so far:
Screenshot 2024-02-06 212539


I want to make it so the text disappears after lets say 2 seconds when the entire text is typed out. Also, I want to make it so the player is still (cannot move) while the typewriting effect is going on
I also want to make a part that detects how far a player is and when the player is within a certain distance of that part, the typewriting effect appears, (for different levels)
Also, i tried setting the gui to not visible, but I the typewriting effect didn’t work. Should I keep it visible or not?
Thank you so much for your help so far, I appreciate it

2 Likes

So after for loops ended, you can pretty much put the wait below the for loops and set the frame visibility to false, if you’re still not familiar with setting the properties then here’s how you do it

local txt = script.Parent
local fram = txt.Parent
local function typewrite(obj, txt, timer)
    fram.Visible = true --if disabled, enable to display the frame since text is inside the frame
    for ind = 1, #text, 1 do --for loops wont continue until it finished running or loops are broken through the "break" syntax
          object.Text = string.sub(text, 1, i)
          task.wait(0.05) 
    end
    task.wait(2)
    fram.Visible = false --if the object specified has a property called "Visible" then set it false
end
2 Likes

I just so have happened to be working on my own dialogue system you can check out here.

I made use of a typewriter function made by a Roblox staff member themself. It came without any issues for me.

It comes with a ton of customizability room and editable properties.
If you need further explanations, study the staff’s post and my post, but if that doesn’t answer your question then I can help you further.

Try this! It’s a little choppy, but it should work with a debounce or something.

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")

local Frame = script.Parent.Parent
local TriggerPart = workspace:WaitForChild("TriggerPart") -- Change this to the path to your part

local function AutoText(object, text)
	for i = 1, #text, 1 do
		object.Text = string.sub(text, 1, i)
		script.Parent.Sound:Play()
		task.wait(0.05)
	end
end

TriggerPart.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChildOfClass("Humanoid") then
		Frame.Visible = true
		AutoText(script.Parent, "test test test test")
		task.wait(2)
		Frame.Visible = false
	end
	
	if Frame.Visible == true then
		Humanoid.WalkSpeed = 0
		Humanoid.JumpPower = 0
	else	
		Humanoid.WalkSpeed = 16
		Humanoid.JumpPower = 50
	end
end)
1 Like

This is the result im getting


what should I do from here?

1 Like

You do have more letters in your text, right? I don’t see why it would do this.

Yes, I do have more letters in my text

1 Like

Can I see your script? Did you make any changes or just copy and paste mine?

Nope, its the exact script that you sent me, and I did name my part to triggerpart

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild(“Humanoid”)

local Frame = script.Parent.Parent
local TriggerPart = workspace:WaitForChild(“TriggerPart”) – Change this to the path to your part

local function AutoText(object, text)
for i = 1, #text, 1 do
object.Text = string.sub(text, 1, i)
script.Parent.Sound:Play()
task.wait(0.05)
end
end

TriggerPart.Touched:Connect(function(hit)
if hit.Parent:FindFirstChildOfClass(“Humanoid”) then
Frame.Visible = true
AutoText(script.Parent, “hello hi”)
task.wait(2)
Frame.Visible = false
end

if Frame.Visible == true then
	Humanoid.WalkSpeed = 0
	Humanoid.JumpPower = 0
else	
	Humanoid.WalkSpeed = 16
	Humanoid.JumpPower = 50
end

end)

1 Like

Or use the easier (and official) code I linked in my previous post…

local function removeTags(str)
	-- replace line break tags (otherwise grapheme loop will miss those linebreak characters)
	str = str:gsub("<br%s*/>", "\n")
	return (str:gsub("<[^<>]->", ""))
end

local displayText = removeTags(script.Parent.LocalizedText)

local index = 0
for first, last in utf8.graphemes(displayText) do 
	local grapheme = displayText:sub(first, last) 
	index += 1
	-- Uncomment this statement to get a reveal effect that ignores spaces.
	-- if grapheme ~= " " then
		script.Parent.MaxVisibleGraphemes = index
		wait()
	-- end
end
1 Like

I was able to get everything to work
essentially a player is only able to get a specific dialogue (lets just say level 1 dialogue) once. But I found that if a player resets their character, they can do the dialogue again. I’ve been trying to fix that but I want unsuccessful. Could you help me? Here is my script:

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild(“Humanoid”)

local TriggerPart = workspace:WaitForChild(“TriggerPart”)
local Frame = script.Parent.Parent

local typewritingTriggered = {}

function AutoType(textLabel, message)
for i = 1, #message do
textLabel.Text = string.sub(message, 1, i)
task.wait(0.07)
script.Sound:Play()
end
end

TriggerPart.Touched:Connect(function(hit)
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if player and not typewritingTriggered[player] then
typewritingTriggered[player] = true
Frame.Visible = true
Humanoid.WalkSpeed = 0
Humanoid.JumpPower = 0

	AutoType(script.Parent, "The evil dentist has gone crazy! Let's try and find a way to escape!")
	wait(#"hello hi" * 0.1)  

	Frame.Visible = false
	wait(0)  

	Humanoid.WalkSpeed = 16
	Humanoid.JumpPower = 50
end

end)

1 Like
  1. Part/hitbox to trigger it
  2. Lock the players position and camera
  3. Tween camera to a certain position you want
  4. Start dialogue
  5. when dialogue ends unlock player and reset camera
1 Like

I have this issue before but solved by someone.

Here’s the töpic I made :

looks different but I made it the talking system idk ahh

My keyboard is Finnish

1 Like

Yeah but I don’t think that’ll help, as i’m trying to make it so a player can only get the dialogue once, while you want it so you can get it more than once.

1 Like

Make something that limits the NPC dialogue to the player.

Like numbers value, bool value, etc…

If they reached their goal, then stop it.

1 Like

I have no idea how to script, could you help?

1 Like

Server script

-- Suomea hetki

local Remote = game.ReplicatedStorage:WaitForChild("RemoteEvent")
local Bool = false

-- Something that triggers.. Example :

-- Part.Touched:Connect(function(hit) --(if using part)
-- ProximityPrompt.Triggered:Connect(function(player) --(if using prompt)

Remote:FireClient(players argument, Bool)

end)

Localscript

local Remote = game.ReplicatedStorage:WaitForChild("RemoteEvent")

Remote.OnClientEvent:Connect(function(Bool)
    if Bool == true then
         -- something about dialogue here (cloning UI, talking, etc...
    end
end)

-- Siellä on koodini :)