Problem with typewriter effect

Hey! My typewriter effect has couple issues. It sometimes types in a really glitchy way and the sound often does that as well, or completely breaks (for example plays only for like 0.2 seconds).

Also, the hitreg for the GUI touch part is a little bit off. When you leave (TouchEnded), it’s good, but on the start (Touched), it just doesn’t feel smooth, as it also takes more time to show up.

Typewriter Module Script

local module = {}

function module.typeWrite(object, text, length, sound)
	for i = 1, #text, 1 do
		object.Text = string.sub(text,1,i)
		sound:Play()
		wait(length)
	end
end

return module

Typewriter Module Function call

local TypewriterModule = require(script.ModuleWriter)
local writeSound = script.Typewriter

TypewriterModule.typeWrite(object, "Yo! I'm Frank, the Construction Manager.", .02, writeSound)

Video showcasing the problem

I can also provide you with the script for the touch part hitreg, if needed.

1 Like

What is the sound id for testing.
a pre guess … task.wait(length)

1 Like

rbxassetid://140910211

characterss

1 Like
  1. Use MaxVisibleGraphemes.
  2. Try playing the sound before the loop, not in the loop.
2 Likes

Playing it before the loop doesn’t really make sense. It should basically play for each new character, to give it the feeling of the typewriter effect. That’s why it’s in the loop for the characters.

1 Like

Also not sure for the MaxVisibleGraphemes property, as I’d like to be able to showcase the full message. Thanks though.

1 Like

I figured the audio was actually a long sequence, since it abruptly stops in the video.

But you can?

1 Like

Well if I limit the number of characters to a certain number, then it won’t display the whole message, will it?

1 Like

Use MaxVisibleGraphemes and also make sure it isnt repeatedly running the Touched event when you interact with the circle.

local module = {}
local tweenService = game:GetService("TweenService")

function module.typeWrite(object, text, length, sound)
	local finished = false
	local txt_length = string.len(text)
	tweenService:Create(object,TweenInfo.new(txt_length*length),{MaxVisibleGraphemes=txt_length}):Play()
	task.spawn(function() repeat task.wait(length) sound:Play() until finished = true end)
	tween.Completed:Wait()
	finished = true
end

return module

There is a code sample right under the MaxVisibleGraphemes documentation:

local TweenService = game:GetService("TweenService")

local textObject = script.Parent

local tweenInfo = TweenInfo.new(
	4, -- it takes 4 seconds for the effect to complete
	Enum.EasingStyle.Sine, -- typing starts fast and slows near the end
	Enum.EasingDirection.Out
)

local tween = TweenService:Create(textObject, tweenInfo, {
	-- Final value should be the total grapheme count
	MaxVisibleGraphemes = utf8.len(textObject.ContentText),
})

tween:Play()
tween.Completed:Wait()

-- Reset the value so it can be tweened again
textObject.MaxVisibleGraphemes = -1

Yeah, looking at the video closer, I now realise that the issue is no debounce on the hit registration script. Make sure you add a debounce.

There is debounce though.

characters

Seems work fine with a wait as long as the sound … try switching .02 to .05 or .06
suggest using task.wait

If all else fails …

sound:Play()
while sound.IsPlaying do
    task.wait(0.01)
end		

You do want it to bunch up at times as that sounds more realistic .05 sounded good to me.

Could I view the script for said debounce?

local dialogueDebounce = false
local progressDebounce = false

local TypewriterModule = require(script.ModuleWriter)
local writeSound = script.Typewriter

local RS = game:GetService("ReplicatedStorage")
local EffectEvent = RS:WaitForChild("ConstructionSoundBlur")

script.Parent.Touched:Connect(function(hit)
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	if player then
		local HasAcceptedYet = player.ConstructionFolder.HasAcceptedYet
		if HasAcceptedYet.Value == false then
			if dialogueDebounce == false then
				local gui = player.PlayerGui.EventDialogue
				gui.Holder.Visible = true
				gui.Holder:TweenPosition(UDim2.new(0.5, 0, 0.759, 0), "In", "Linear", 1, true, nil)
				wait(1)
				gui.Holder.Header.Visible = true
				gui.Holder.DialogueText.Visible = true
				dialogueDebounce = true
				wait(1)
				local object = gui.Holder.DialogueText
				TypewriterModule.typeWrite(object, "Yo! I'm Frank, the Construction Manager.", .02, writeSound)
			end
		elseif HasAcceptedYet.Value == true then
			if progressDebounce == false then
				local gui = player.PlayerGui.ConstructionGui
				local sound = player.PlayerGui.HUD.Sidebar.Container.Construction.LocalScript.ClickSound
				gui.Holder.Visible = true
				gui.Holder:TweenPosition(UDim2.new(0.5, 0, 0.5, 0), "In", "Elastic", 1, true, nil)
				EffectEvent:FireClient(player, 15)
				wait(1)
				progressDebounce = true
			end
		end
	end		
end)


script.Parent.TouchEnded:Connect(function(hit)
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	if player then
		if player:FindFirstChild("PlayerGui") then
			local HasAcceptedYet = player.ConstructionFolder.HasAcceptedYet
			if HasAcceptedYet.Value == false then
				if dialogueDebounce == true then
					local gui = player.PlayerGui.EventDialogue
					gui.Holder.DialogueText.Text = ""
					gui.Holder.Header.Visible = false
					gui.Holder.DialogueText.Visible = false
					gui.Holder:TweenPosition(UDim2.new(0.5, 0, 1.5, 0), "In", "Linear", 1, true, nil)
					wait(1)
					gui.Holder.Visible = false
					dialogueDebounce = false
				end
			elseif HasAcceptedYet.Value == true then
				if progressDebounce == true then
					local gui = player.PlayerGui.ConstructionGui
					local sound = player.PlayerGui.HUD.Sidebar.Container.Construction.LocalScript.ClickSound
					gui.Holder:TweenPosition(UDim2.new(0.5,0,1.5,0), 'InOut', 'Elastic', 1)
					EffectEvent:FireClient(player, 0)
					wait(0.8)
					progressDebounce = false
				end
			end
		end
	end
end)

You only apply the debounce one second after it starts the typewriting effect. Move it up to the moment the tween starts.

So like this?

if HasAcceptedYet.Value == false then
			if dialogueDebounce == false then
				local gui = player.PlayerGui.EventDialogue
				gui.Holder.Visible = true
				gui.Holder:TweenPosition(UDim2.new(0.5, 0, 0.759, 0), "In", "Linear", 1, true, nil)
				wait(1)
				gui.Holder.Header.Visible = true
				gui.Holder.DialogueText.Visible = true
				wait(1)
				dialogueDebounce = true
				local object = gui.Holder.DialogueText
				TypewriterModule.typeWrite(object, "Yo! I'm Frank, the Construction Manager.", .02, writeSound)
			end

No, like this:

if HasAcceptedYet.Value == false then
			if dialogueDebounce == false then
				local gui = player.PlayerGui.EventDialogue
				gui.Holder.Visible = true
				gui.Holder:TweenPosition(UDim2.new(0.5, 0, 0.759, 0), "In", "Linear", 1, true, nil)
				dialogueDebounce = true
				wait(1)
				gui.Holder.Header.Visible = true
				gui.Holder.DialogueText.Visible = true
				wait(1)
				local object = gui.Holder.DialogueText
				TypewriterModule.typeWrite(object, "Yo! I'm Frank, the Construction Manager.", .02, writeSound)
			end

Also, I would recommend using task.wait(1) instead of wait(1)

Ah, alright. Though, it still doesn’t seem to work. Contrarily, it doesn’t even display the message now (after I remade the module script).

try this:

local module = {}

function module.typeWrite(object, text, length, sound)
	for i = 1, #text do
		object.Text = string.sub(text,1,i)
		sound:Play()
		task.wait(length)
	end
end

return module
local TypewriterModule = require(script.ModuleWriter)
local writeSound = script.Typewriter

TypewriterModule.typeWrite(object, "Yo! I'm Frank, the Construction Manager.", writeSound.TimeLength, writeSound)
1 Like