Typewriter Effects: 2 Methods

Typewriter Effects: 2 Methods

by: @DasKairo


Update #41

Update Stated Below:

Updates on wording after some Update


This is a remake of a Topic I made back in November 22, 2022.

By Remake, I mean that I completely rewritten it. The Orginal Took at about an Hour or so to make, the rewrite took me 6 hours to do. What am I doing with my Life?

This is the Topic to I am refering to, to be exact:

I Get it, this was a Stupid thing to do, Create and entirely new Topic instead of Editing it, and I understand why, but here I wanted to add a Bit more Detail to the already existing Topic and just restart it with more Details and Decoration to it, Also, The Updates Made wont bump the Topic, and Bumping it more via Reply wouldn’t be a very good idea.
I dont really want to mess around with older Replies on the Post, Because it would just make them off topic from the Actual Topic

However, That isnt an Excuse for why I made a Completely new Topic, and you can get mad at me all you want about it.
But If you want to view the Old version, you can, But this is more of a 2.0 of it maintaining a lot of the Information, Except Probably more Detailed and Decorated.


Table Of Contents

(Broken. idk why)


Introduction

Hello!

This is just a Tutorial on how to Make a Simple Typewriting Effects on ROBLOX if you didnt know how, or you want Someone to look at for a better understanding.

This isn’t really intended to be a Advanced Tutorial, but more of a Basic, and Simple Tutorial you can use for work, or for fun. There are obviously better ways you can make typewriter effects usually involving more understanding of specific things, but for this, not really, However you are allowed to share your Creations and there is nobody stopping to you so.

Note that I may not be accurate with that I say in this Topic, Some of the Definitions of things are Simplified to help with Understanding and may be off from the Actual meaning of what it actually does (or is), So If I do make mistakes, feel free to Correct me or let me know of my mistake.

This is mostly coming from me (A very Horrible Scripter) with a Little Help of Something called Roblox Documentation.

Anyway, Back to the Topic.


When It comes to this, you may be wondering where to use it? Here is a list of Places where you can actually use this Script, and The Scripts you can use it for:

Places to use this Script: Script Types you can use:
ServerScriptService Script
StarterGui LocalScript
About Anywhere! ModuleScript

But about the "Roblox Documentation, " These are Sources from which I got this information, Along with a Model Kit I made In case you are lazy and dont like using Tutorials by other people. How Rude… :rage:

Resource: Link:
string Library string | Roblox Creator Documentation
MaxVisibleGraphemes New Property: MaxVisibleGraphemes
Model Kit (By me!) Typewriter Effects - Roblox

“Beginner” Things:

When Creating a Typewriting effect, Keep in mind that the method you are doing, probably isnt the best one out there (Like this Topic), But one of the worst ways (if not “the” worst) to create a Typewriting effect would be this:

Text = "" -- starts off with an Empty String
wait(.1) -- "yields" for .1 of a Second (1/10) with throttling
Text = "H" -- Adds a Character
wait(.1)
Text = "He"
wait(.1)
Text = "Hel"
wait(.1)
Text = "Hell"
wait(.1)
Text = "Hello"
wait(.1)
Text = "Hello!" -- End Result
wait(.1)

There are many discrepancies between the Methods I’ll Cover, and This, along with many Issues. This is very inefficient to be doing and is something that a “Beginner” would do, this also relies on the usage of a Deprecated Global, wait() is Deprecated now, and should be Replaced with task.wait(), A faster and more accurate yielding method than wait(), it also takes a long time to write and takes way more lines compared to the Methods We will Cover, not a good thing, its understandable that “it makes it easier to read.” but that argument isnt very good when it comes to usage, it makes your code look ugly instead of more cleaner.
With the Code in this Tutorial, It should Simplify this usage, and should be more efficient using more recent methods.

String Length:

String length (referring to the size which is the amount of characters a string holds) is what we will be using to help use keep track of how much we need to repeat our code, this can be done with multiple functions, along with the usage of an operator, like for Example:

local myString = "Hello world!" -- we have our string here

local charCount = string.len(myString) 
-- gets the Length of the string via string function

-- Method 2:

local charCount = myString:len()
-- gets the Length of the string via function for class

-- Method 3:

local charCount = #myString
-- gets the Length of the string via '#' operator
-- this would generally be the fastest method

print(charCount) --> 12 (Characters in the string including spaces)

And if you don’t want it to include spaces, you can always use string.gsub(), like this:

local myString = "Hello world!" -- our string
local removedSpaces, charactersReplaced = string.gsub( -- gsub function
    myString, -- The String you want to change
    " ",      -- Pattern you want to Replace
    "",       -- Replacement 
)
print(#removedSpaces) --> 11 (Characters in the string without spaces)
print(charactersReplaced) --> 1 (Amount of Characters Changed)

These are all Correct, but they arent all the same, and others are more efficient than others, # is the likely choice due to its simplicity and its speed, it can also be used to check the number of items in a table, like so:

t = {"Apples"} -- table (this type is known as an 'Array')
print(#t) --> 1 
-- ("Apples" is inside the Table, if it wasnt, it would be 0)

However, this table Example is not Important or Relevant to the Heading, so I’ll Continue.

Function Usage:

For the Purpose of us not repeating our code and having to rewrite them everytime we want to use them, we can use functions for this, so they are reusable for us.
This however, you should already know this.

opening keyword closing keyword
function end
function func()
end

-- Alternate Method:

local func = function()
end

Loop Usage:

We use a little something known as Loops to help us with repeating code, As of Currently we have 3 types of loops:

Loop keyword Closing keyword
while end
repeat until
for end

So Instead of us constantly writing the exact same line for a certain amount of times, we can utilize these to our advantage, here is how you set them up:

while Condition do -- while a condition is true, it will repeat
end

repeat
until Condition -- repeats until a condition is false

for i = 1,10 do -- repeats a certain amount of times
end

for index, value in Array do -- repeats for how many items are within a table
end

It is best to note that these loops will yield until they are broken with the usage of the break method, however now we have to set the speed we want our loop to go, which we have to apply ourselves, if we dont do this, our game will crash completely, and cause a timeout, which isnt what the want with our code, so in order to prevent this, we will use task.wait() to yield how times our loop will go through.

while Condition do

    task.wait(.1) -- yields .1 seconds before Moving to the next Iteration
end

Now, you may be wondering: What Is an Iteration?

An Iteration in simple terms is A Sequence of code that is repeated until a specific result is acheived. Which will help us with our Typewriter effect.


MaxVisibleGraphemes

MaxVisibleGraphemes purpose is intended for the usage of Typewriting effects. So It wouldn’t make sense that we wouln’t have it here,.So for this, I will show you how to make the Typewriting effect for this Tutorial,.

We First Start off with either a TextLabel or a TextButton here, With these Instances, we can use them to typewrite text onto them using their Text Property, you can either Assign a Variable to an Already Existing Instance or you can create a brand new one using Instance.new(), For the Tutorial However, it will be known as Frame.

We will also be using a function to store our code, like this:

local function Typewrite(str: string, speed: number)
    -- 'str' is our string we will be typewriting
    -- 'speed' is the speed per each addition

    -- Keep Reading, you're almost there!
end

This is help us reuse the function whenever we want to use instead of having to write it al over again, which isn’t good practice.

The Second Thing we will do here is Apply our Text, which is as simple as you think, you can use the Text property of your TextLabel or TextButton to have text within them, so within our function, we can Apply it as such:

Frame.Text = str -- Applies our string to the Frame

Keep in mind that it will not work if you do this:

local text = Frame.Text -- string
text = str -- string

What this is doing is returning text for our Variable, and we are only changing our Variable to another string, which isn’t what we want, we want to apply to a Property, not get the Value of a Property, if that makes sense.

The Second Property we will be Modifying is the MaxVisibleGraphemes Property, which basically is how many characters are visible at a time, by Default, it will be set to -1 which makes all the Characters within the Frame Visible at once.

So if we set this Property to 0, It will show 0 characters, don’t get scared about that, you are following this Tutorial very well, So now if we set this Property to 1, It will now show one character, so you can think of it like this:

------------------------------------------------------------------------------
-- Original Properties of our Text
OriginalText = "Hello!"
#OriginalText  --> 6 (6 Characters Total)
------------------------------------------------------------------------------
-- How Its Rendered by TextLabel / TextButton
RenderedText = "      " -- MaxVisibleGraphemes = 0 (0/6 Characters)
RenderedText = "H     " -- MaxVisibleGraphemes = 1 (1/6 Characters)
RenderedText = "He    " -- MaxVisibleGraphemes = 2 (2/6 Characters)
RenderedText = "Hel   " -- MaxVisibleGraphemes = 3 (3/6 Characters)
RenderedText = "Hell  " -- MaxVisibleGraphemes = 4 (4/6 Characters)
RenderedText = "Hello " -- MaxVisibleGraphemes = 5 (5/6 Characters)
RenderedText = "Hello!" -- MaxVisibleGraphemes = 6 (6/6 Characters)

Now if you add one more to MaxVisibleGraphemes, which would be 7, it would render anymore Text as there is no more to Render until you add more text, if you want the same text to repeat, you can use string.rep or concatenate the Variable to Itself.

Obviously, as you can tell this isn’t a very good Example for MaxVisibleGraphemes, nor what It actually is, its more of for you to play with the Actual Properties so you can get a Better Understanding of them, But it would get the job done for explaining “Invisible Text”, but anyway:

We now have a Understanding on what MaxVisibleGraphemes is, Goob Job!
With this new Knowledge, we can know Apply it to our Script, Like so:

local function Typewrite(str: string, speed: number)
    Frame.Text = str -- Applies string for usage
    Frame.MaxVisibleGraphemes = 0 -- Sets Visible Characters to 0

   -- There is still more to come :)
end

So now, we have about half of our Script done, We now need to loop through how many times for how big the text is, yep you guessed it.

For the Third thing, we will now get the Length of the String using the # operator, which will return as 6 according to the number of characters in our string; Hello!, so how will this help us? from here we will use a loop known as a for loop to keep track of the amount of iterations we will do, so with the loop, we will write this inside the function:

for i = 1, #str do -- this will repeat for the amount of characters within the string
    -- code in a sec
end

So here, we will now add 1 to MaxVisibleGraphemes so for each iteration, there would be 2 ways you can do the following

MaxVisibleGraphemes = MaxVisibleGraphemes + 1 -- if we have 0, it would be 0 + 1 which is 1

MaxVisibleGraphemes += 1 -- Simplified version

Frame.MaxVisibleGraphemes = i 
-- Adds Graphemes (Characters) based on how many times the for loop ran

The i method requires the usage of the for loop as we have it set there, since we have it as 1, out of 10, it will continue to add up per iteration until it reaches the expected number (which is 10) which is what the i = 1,10 part of the loop does, or it breaks with the usage of break, keep in mind continue doesnt actually break the loop, it skips the iteration its in, if you are on point 1, it will skip to point 2.

So now If we integrate this chunk of code into out code, it will look like this:

local function Typewrite(str: string, speed: number)
    Frame.Text = str
    Frame.MaxVisibleGraphemes = 0

    for i = 1, #str do -- for loop
         Frame.MaxVisibleGraphemes += 1 -- Adds Graphemes (Characters)
    end
end

The Next- Oh… We forgot a very Important thing, Always remember to add task.wait() when using a for loop. If you have a very long string, It will run this code so many times causing the game to crash, or cause a time out within the code, with task.wait(), we can prevent this from happening, so this is what it will look like:

for i = 1, #str do
    Frame.MaxVisibleGraphemes += 1
    task.wait(speed) -- this is where the 'speed' variable comes in
end

So, with that out of the way, you can now Test the code, Which this should be the full code:

local function Typewrite(str: string, speed: number)
    Frame.Text = str -- Applies string for usage
    Frame.MaxVisibleGraphemes = 0 -- Sets Visible Characters to 0

    for i = 1, #str do -- this will repeat for the amount of characters within the string
        Frame.MaxVisibleGraphemes += 1 -- Adds Graphemes (Characters)
        task.wait(speed) -- yields with the speed given
    end
end

So Basically now, you can write this using the function:

Typewrite("This is a Typewrite Effect!", .1)
-- "This is a Typewrite Effect!" is the string to be used
-- .1 is the amount of time the code will yield per iteration

Which Should Be all for the MaxVisibleGraphemes version of the Tutorial :slight_smile:


string.sub

For the Purpose of the string.sub(), You can follow the MaxVisibleGraphemes as it would be very Similar to this, This will Mainly be Modifying the MaxVisibleGraphemes Version of the code, remember this is also slighly more “Advanced” than MaxVisibleGraphemes, so sorry if I make this confusing.

From the Last Tutorial, We have this code;

local function Typewrite(str: string, speed: number)
    Frame.Text = str -- Applies string for usage
    Frame.MaxVisibleGraphemes = 0 -- Sets Visible Characters to 0

    for i = 1, #str do -- this will repeat for the amount of characters within the string
        Frame.MaxVisibleGraphemes += 1 -- Adds Graphemes (Characters)
        task.wait(speed) -- yields with the speed given
    end
end

Now, What do you do with this? Well, here I will show you how to Modify the Code

First thing we would do is Swap out MaxVisibleGraphemes with string.sub(), But now, We must know the Arguments, there are 3 of them to be Exact:

s is the string you are using, which in our Example, would just be Hello!
i is the Amount you want to start at, The Default is at 1 which is the first Character of the string
j is the Amount you want to end at, The Default is -1 which would be the last Character of the string

But, when we set j to 0, it will be invisible, and if we set j to 1, it will only show the first character, and so on.

So knowing this, within the code we can use the for loop to have i continue until j, which in this case, i in our for loop (which keeps tracks of the iterations) can be used to have the i Argument continue to that number, so for this purpose, I will name i within the for loop, index so you can define it:

for index = 1, #str do -- this will repeat for the amount of characters within the string
    Frame.Text = string.sub(str, 1, index) -- Adds Characters until it reaches index
    -- so if it ran 3 times, it will show 3 Characters because 'index' is equal to 3
    task.wait(speed) -- yields with the speed given
end

So now, you dont need to Apply the Text Early, you can instead use string.sub to apply the substring of the string, This should be the full code:

local function Typewrite(str: string, speed: number)
    for index = 1, #str do -- this will repeat for the amount of characters within the string
        Frame.Text = string.sub(str, 1, index) -- Adds Characters until it reaches index
        -- so if it ran 3 times, it will show 3 Characters because 'index' is equal to 3
        task.wait(speed) -- yields with the speed given
    end
end

So now, you can test the Code how you tested with MaxVisibleGraphemes which should Be the Exact same thing:

Typewrite("This is a Typewrite Effect!", .1)
-- "This is a Typewrite Effect!" is the string to be used
-- .1 is the amount of time the code will yield per iteration

And I believe that should be all for string.sub() :slight_smile:


Optionals

There are some Optional Things you can do to the code, Keep in mind that these may cause Issues with your code depending on how you handle them.

Note that The code blocks are all Example code, not the Actually code, as for an Example.

Preventing Yielding

If you ever want to prevent yielding, you can use coroutines or task for this purpose, this would have the code run alongside the Main Code using threads, which would prevent yielding from the code, The only yielding that will happen is the code within the thread, but it shouldn’t cause issues.

If you have the same function run right after, It may cause the Issue of two Typewriting Effects Happening at the Same time, which would not look great.

Setting Default Values

Within your code, you can set Default values of items, so when you dont add a Argument inside the function, you can simply say to give another value instead:

function ex(x: number)
    print(x or 1) -- if x is nil (no value given) it will print "1" instead
end)

ex(2)   --> 2 (value given)
ex()    --> 1 (no value given)
ex(nil) --> 1 (no value given)

Ending functions and or returning Data

You can end a function early if a condition isnt met, or to return a piece of Data, Like this:

-- 'return' ends a function early, but can return Data
function ex(x, y)
    local random = math.random(x, y)
    if random ~= 1 then
        return false -- returns a boolean
    end
    -- if statement can be simplified to:
    return (random == 1) -- returns a Boolean
end

local byChance = ex(1, 10) -- Variable Assigned to returned Data
if byChance then -- if true
    print"got 1 by chance!"
end

Polls

If you want, you can Answer these, These are just what you think, They are very much Optional.

What do you think?
  • Useful
  • Good
  • Ok
  • Bad
  • Terrible

0 voters

How would you Rate it on a Scale of 1 to 10?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

0 voters

If you think It’s Bad, You can always say why! :slight_smile:

16 Likes

Please don’t encourage users to use functions without parentheses. It makes your core less readable and goes against the Luau Style Guide

2 Likes

I only do that if I am only going to use a string by itself, or if there is only one argument, I dont do it all the time. But good to know

In certain situations, you’d want to use utf8.graphemes for iteration because certain characters and symbols (such as emojis) can have multiple codes assigned to them.

1 Like

I was thinking about adding that a while ago, but yeah you could

Hello, I am a starting scripter, and my problem was that the text was appearing like too slow, and then when you explained how task.wait() work that actually saved me a lot of time. I have read the whole tutorial, it’s wonderful, you’re really talented, keep up the good work! :smile:

1 Like