Typer Writer that supports Rich Text

Hello, i would like to introduze my own typer writer module, wich support audio and rich text. Feel free to use in your game.

--[Module]
local TyperWriter = {}
TyperWriter.__index = TyperWriter

--Creates new type object.
function TyperWriter.new(Settings)
	local self = setmetatable({}, TyperWriter)
	if not Settings.Text then
		return error("Text cannot be nil.");
	end
	self.Text = Settings.Text
	self.Delay = Settings.Delay or 0.2
	self.Sound = Settings.Sound or ""
	self.Object = Settings.Object or nil
	self.Callback = Settings.Callback or nil
	self._isRunning = false
	self._startCalled = false
	self.Done = false
	if not Settings.Object then
		return error("Object cannot be nil.");
	end
	if not Settings.Object:IsA("TextLabel") or Settings.Object:IsA("TextButton") then
		return error("Object have to be an TextSource.");
	end
	
	return self;
end

--Dismiss or delete typer.
function TyperWriter:Dismiss()
	self._isRunning = false
	self = nil
end

--Start or resume typing.
function TyperWriter:Start()
	if self._startCalled then
		self._isRunning = true
		return;
	end
	
	task.spawn(function()
		self.Object.Text = self.Text
		self.Object.MaxVisibleGraphemes = 0
		local length = 1
		for _ in utf8.graphemes(self.Object.ContentText) do
			length += 1
		end
		
		local current = 0
		self._isRunning = true
		self._startCalled = true
		
		local sound
		if self.Sound then
			sound = Instance.new("Sound")
			sound.Name = "Sound"
			sound.PlayOnRemove = false
			sound.SoundId = self.Sound
			sound.Parent = self.Object
			sound.Looped = false
		end
	
		while true do
			if not (self._isRunning) then
				task.wait(0.1)
				continue;
			end
			if current >= length then
				self.Done = true
				self._isRunning = false
				if self.Callback then
					task.spawn(self.Callback)
				end
				break;
			end
			self.Object.Text = self.Text
			self.Object.MaxVisibleGraphemes = current
			current += 1
			if sound then
				sound:Play()
			end
			task.wait(self.Delay)
		end
		if self.Done then
			self._isRunning = false
			self._startCalled = false
			self:Dismiss()
		end
	end)
end

--Pause typing.
function TyperWriter:Pause()
	self._isRunning = false
end

return TyperWriter;

Here an exaple of how you can use it:

--[Services]
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--[Modules]
local TyperWriter = require(ReplicatedStorage.TypeWriter)

--[Main]
function callback()
	print("done")
end

local Typer = TyperWriter.new({
	Text = [[<font color="rgb(255,255,0)">MrRightDev:</font> This is an example. 😍]], --The text you want.
	Delay = 0.1, --The time between each type.
	Sound = "rbxassetid://9083627113", --SoundId, It requieres to be an url.
	Object = script.Parent, --The object you want to.
	Callback = callback
})

Typer:Start() --Start the typing.
task.wait(0.3)
Typer:Pause() --Pause the typing.
task.wait(0.3)
Typer:Start() --Resume the typing.
task.wait(0.5)
Typer:Dismiss() --Dismiss the typing, Typer is now nil.

If you want give me the credit :slight_smile:

1 Like

It feels like you are complicating something that’s already so simple to begin with.

The .MaxVisibleGraphemes property already supports richtext. You can just tween that value to the length of ContentText, and then hook a PropertyChanged signal to play the sounds. Tweens already have support for canceling and pausing.

It’s a solid effort, but it doesn’t look practical, especially with the OOP structure, which just feels unnecessary.