Roblox Wiki
Register
Advertisement
Roblox Wiki
Tutorial page
This article is an advanced tutorial.

Welcome to Advanced Scripting! This tutorial will dive into more technical terms and concepts of scripting Roblox. Here's a quick review of what we've learned so far.

Review[]

Newbie's Guide[]

  • The Home Tab contains several tools to insert and manipulate shapes.
  • Three-dimensional shapes in Roblox are called parts, and you can modify its shape, size, colour, texture and name.
  • A script contains the code.
  • The Explorer window is the list of all the objects in the game, and it has a hierarchal structure.
  • To create a path to an instance, you start with the word game then work your way down the hierarchy.
  • In the Explorer window's hierarchy, a parent is one step above its child.
  • A complete statement must perform an action to an instance.

Beginner's Guide[]

  • The properties of an object determine its visible characteristics.
  • The output window is the primary tool for fixing coding errors.
  • A variable is a piece of text representing numbers, text, booleans or instances.
  • Variables can be declared, updated and accessed.
  • The .new() constructor creates a new value for certain properties.
  • Vector3 is a value type that determine the coordinates of an object in a three-dimensional space.
  • A function is a piece of code that can be used multiple times.

Intermediate Guide[]

  • An if statement performs an action if a condition is met.
  • If statements can be nested and paired with else and elseif statements.
  • A service is an instance of the highest hierarchal level in the Explorer Window.
  • A function can be paired with parameters, and multiple parameters are called tuples.
  • Functions can return values in the form of variables.
  • Many instances have built-in functions for the scripter to use.
  • A table is an array of values wrapped by curly brackets {}.
  • A loop executes code multiple times.
  • Scripts are indented according to its blocks.

Values[]

So far, we've learned that you cannot access variables from other scripts. But what if you have to? You can use a value. These are variables that are not inside a script, but you add to the Explorer Window. You can do this by inserting it to a part. There are several kinds of values, but you get the idea. To access it, just reference it like you would with a part.

To set the value in a script, you change its Value property.

script.Parent.BoolValue.Value = false

An extremely common mistake made when setting values is that developers often forget to add ".Value" to their script. This is required because it is the Value property that you are trying to set.

There are several types of Values: BoolValues, StringValues, IntValues, NumberValues, and CframeValues.

Now, here's the thing: If something has many Value objects, for the sake of organization, we like to put this into a Configuration folder.

Of course, like normal folders, this is entirely optional, but if you choose to use it you have to make a path like this:

game.Workspace.Part.Configuration.BoolValue.Value = false

Leaderstats[]

Let's say you were making Monopoly in Roblox and wanted to make a leaderboard on the top right of your game to record the players' squares and cash. Start by making the leaderboard by adding a folder named leaderstats parented to a player whenever they joined. We use the PlayerAdded event in the Players service.

game.Players.PlayerAdded:Connect(function(player)
     local leaderstats = Instance.new("Folder")
     leaderstats.Name = "leaderstats"
     leaderstats.Parent = player
end)

Note that the name of the folder must be "leaderstats"; Roblox won't accept any other variation.

Now simply add IntValues, StringValues, NumberValues and BoolValues to make actual statistics.

We want to make stats for the current square the player is on, as well as the amount of cash given. A StringValue would work best for the Location, and an IntValue would work best for the amount of cash, assuming you don't want to use a dollar sign ($) in the stats.

So, we create both values using the .new constructor, then change its Name and Parent.

game.Players.PlayerAdded:Connect(function(player)
     local leaderstats = Instance.new("Folder")
     leaderstats.Name = "leaderstats"
     leaderstats.Parent = player

     local Cash = Instance.new("IntValue")
     Cash.Name = "Cash"
     Cash.Parent = leaderstats

     local Location = Instance.new("StringValue")
     Location.Name = "Location"
     Location.Parent = leaderstats
end)

Now, traditionally, in Monopoly, we start at the "GO" and receive $1500. So, you set its Value property.

game.Players.PlayerAdded:Connect(function(player)
     local leaderstats = Instance.new("Folder")
     leaderstats.Name = "leaderstats"
     leaderstats.Parent = player

     local Cash = Instance.new("IntValue")
     Cash.Name = "Cash"
     Cash.Value = 1500
     Cash.Parent = leaderstats

     local Location = Instance.new("StringValue")
     Location.Name = "Location"
     Location.Value = "GO"
     Location.Parent = leaderstats
end)

Of course, you would need to manually add Spawn Locations at GO to make Players spawn there, but it would say GO on the leaderboard regardless.

ClickDetectors[]

For parts, a click detector is a detector for when the player hovers his cursor over it. To add one, just insert it into the part, and if you test the game, you can see that the cursor changes whenever you hover over it. However, nothing happens. This is because you never scripted it to do anything. So insert a script into the ClickDetector:

script.Parent.MouseClick:Connect(function()
    print("part clicked")
end)

When you left-click the part, you are triggering the MouseClick event. So, it is connected to the function which prints "part clicked."

Enum[]

Enum, short for enumeration, is a data type that takes one of a set of values. In the Properties window, a property that uses Enum would be represented by a dropdown menu. For a part, the Shape and Material properties both use Enum.

script.Parent.Material = Enum.Material.Neon

This is an alternative to putting "Neon" in strings. This may be optional for things like Material, but is required for things like EasingStyle, which we will explain later.

ScreenGuis[]

A ScreenGui stands for screen graphical user interface. For those who are not as savvy with technology, it is basically like a sticker placed on the screen that is not part of the 3-dimensional space. As such, they are never placed in the Workspace. They are placed in StarterGui. So add a ScreenGui, and you'll see that there should be a tab called UI appearing at the top. Click that tab, and you will see various user interfaces, like text buttons, image labels, and more. Design the GUI as much as you want.

If you want to know how to change the Position and Size using the properties instead of dragging, read here.

The Client-Server Model[]

The client-server model is a way that Roblox manages its games.

A "server" is a sort of powerful computer/system controlling all events that happen in a game. It also manages all players in a game.

The "client" is the device of each individual user.

Replication[]

We all know that a change in a game occurs whenever a property is changed, or when an instance is created or destroyed. There are two kinds of changes: a server-sided one and a client-sided one. A server-sided one is when the change occurs to the server (all players experience this change), whereas a client-sided change occurs to one specific client (only one player notices this change). When you use a normal script, this code causes server-sided changes.

Automatically, server-sided changes are replicated to the client: what this means is that when a server-sided change occurs, the same change occurs to the client.

FilteringEnabled[]

Here's where things get tricky: if you look at the properties of the Workspace, you will find a boolean titled FilteringEnabled. What this means is that while server-sided changes can be replicated to the client, a client-sided change cannot replicate to the server. This serves as a sort of safety precaution in the game: if an exploiter was to hack into the game and make malicious changes, this change will not occur to the other players in the game. Note that you can no longer disable this property; it was forced on in 2018.

Local Scripts[]

A local script is a script that will only affect the client side. They are especially useful for things like UserInputService and ScreenGuis. So, if a player were to click a button that spawns teapots, if a local script is used, only that player will see the teapots.

Let's try using it to make a button print text whenever it is clicked. If you haven't already, create a ScreenGui with a TextButton or an ImageButton. Because only the client(s) see ScreenGuis, always use local scripts in ScreenGuis. In the button, insert the following code in a local script:

script.Parent.Activated:Connect(function()
     print("Button was clicked")
end)

The Activated event fires whenever the user fires their left mouse button on the button. Now, it will print text whenever it was clicked.

Note: These scripts CANNOT be used in the Workspace. Nothing will happen if you try.

Player Objects: Player vs. Character vs. Humanoid[]

One thing you need to understand before continuing is a player object. When a player joins a game, two objects are created in the Explorer.

Character[]

A model is created in the Workspace, which manages your body's physical appearance and clothing items. There are two types of characters: R6 (six joints) or R15 (fifteen joints.) This is called the "character."

For future use, let's see how to build a blank character. In the Plugins tab, there is an option called "Rig Builder." Choose the number of joints you want, and choose the type of character you want. This will produce a blank, grey character in the Workspace, also known as a rig.

Humanoid[]

All players will get a Humanoid object, which is parented to the character. Think of it as an ID tag so you could check if something is human or an NPC. This is why when you use a kill brick, you use this code:

script.Parent.Touched:Connect(function(hit)
    local hum = hit.Parent:FindFirstChild("Humanoid")
    if hum then
        hum.Health = 0
    end
end)

This would check if hit.Parent (the character) has a Humanoid object. Let's find a way to make an NPC move. For a normal part, we would need to use a for loop to make it move. For objects with Humanoids, however, we can use the MoveTo function to move it at a normal pace.

local hum = script.Parent.Humanoid
hum:MoveTo(game.Workspace.EndPart.Position)

This will move the human to the part named "EndPart" in the Workspace. You can also use coordinates like this:

local hum = script.Parent.Humanoid
hum:MoveTo(Vector3.new(x,y,z))

Remember, every time you use coordinates, you either have to use CFrame.new or Vector3.new.

Player[]

A player object is an object that is created in the Players service that manages things like the Player ID and the Backpack.

So, let's find out how to find the corresponding player object from its character. We use the GetPlayerFromCharacter() function to find the player object by inputting the character model.

script.Parent.Touched:Connect(function(hit)
     local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
     if plr ~= nil then
           print(plr.Name)
     end
end)

This script would fire the name of the player who touched the part.

How do we find the character from the player object? There is an Character property of the Player object which would show the corresponding character model.

Remote Events[]

So, with FilteringEnabled, what if you need to do something like make a ScreenGui appear when a part is Touched? Well, add a remote. There are two types of Remotes: RemoteEvents and RemoteFunctions. These are all functions that are fired on the client side. They are always stored in ReplicatedStorage. To fire a RemoteEvent, enter the following code:

script.Parent.Touched:Connect(function(hit)
      local Humanoid = hit.Parent:FindFirstChild("Humanoid")
      if Humanoid then
           local player = game.Players:GetPlayerFromCharacter(hit.Parent)
           game.ReplicatedStorage.RemoteEvent:FireClient(player)
      end
end)

So there are many new terms here. "hit" refers to the part that touched the part; in this case it'd be most likely the player's foot. hit.Parent refers to the foot's parent, aka the character. The second line checks whether or not the character has a Humanoid object: in other words, it checks whether it was a player that touched the object.

The third line checks whether the Humanoid variable is truthy or falsey. A "falsey" value is either false or nil, and everything else is a truthy value. If the Humanoid does not exist, the variable would be nil; therefore, it would be falsey and would not fulfill the condition.

Now, insert a local script into StarterGui and enter the following.

game.ReplicatedStorage.OnClientEvent:Connect(function()
     game.StarterGui.ScreenGui.Enabled = true
end)

I have created a listener, aka the OnClientEvent, which would enable the ScreenGui when the part is touched.

Remote Functions[]

Remote Functions are a lot like Remote Events, only they allow for you to return a variable.

So, instead of saying "FireClient," we say "InvokeClient."

script.Parent.Touched:Connect(function(hit)
      local Humanoid = hit.Parent:FindFirstChild("Humanoid")
      if Humanoid then
           local player = game.Players:GetPlayerFromCharacter(hit.Parent)
           game.ReplicatedStorage.RemoteFunction:InvokeClient(player)
      end
end)

Also, the listener in StarterGui is a little bit different.

local function OpenGui()
       game.StarterGui.ScreenGui.Enabled = true
end
game.ReplicatedStorage.RemoteFunction.OnClientInvoke = OpenGui

We use an equal sign instead of a Connect function.

But what is the difference between InvokeServer and InvokeClient? FireServer and FireClient? Well, when you want to affect something on the server side from a local script (i.e. you want a block to appear when you click a GUI,) then you use Invoke/Fire Server. When you want to affect something on the client side using a server script, use Invoke/Fire Client.

Parts of a CFrame[]

A data type you may not have learned about yet is a CFrame. Each Part has a CFrame. Each CFrame is comprised of 12 numbers. The first 3 numbers are the position of the part. The other 9 values are for rotation, but we do not set these values directly. This is how to set the Position and Orientation of a Part:

script.Parent.Position = Vector3.new(0,0,0) --Sets Position.
script.Parent.Position = CFrame.new(0,0,0) --Also sets Position.
script.Parent.Orientation = CFrame.Angles(0,math.rad(45),0) --Sets orientation.

The math.rad converter will convert degrees to radians. So, if I were to say math.rad(45), the system will convert 45 degrees to radians, about 0.78 radians.

Radians

One radian

To be clear, a radian is a unit of measure that measures about 57.3°, or just less than 1/6 of a full circle. Basically, it's a little pizza slice where all sides are the same.

Other Math Operations[]

You may be familiar with basic arithmetic operators, but there are more advanced ones like math.rad. Here are most of them.

  • math.abs(x): finds the absolute value of x
  • math.sin(x): finds the sine ratio of x
  • math.cos(x): finds the cosine ratio of x
  • math.tan(x): finds the tangent ratio of x
  • math.floor(x): rounds x down to the nearest whole number
  • math.ceil(x): rounds x up to the nearest whole number
  • math.rad(x): converts angles to radians.
  • math.pow(x,y): finds x^y.
  • math.sqrt(x): finds square root of x.
  • math.random(x,y): picks a random integer between x and y.
  • math.deg(x): converts radians to degrees.

You may not be familiar with many of these, but you will learn them in high school mathematics later on.

DataStoreService[]

One of the last things you need to learn to make a fully-functional game is saving player data. You would do this using DataStoreService. Of course, it does not appear in the Explorer, so you need to use GetService().

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("FirstDataStore")

A data store is a place where data is stored while the player is offline. When you use the GetDataStore() function, the first parameter is the name you give the datastore.

Now we need to actually save player data. Let's reuse the Monopoly example from the leaderstats section. At the bottom of your script, you add a PlayerRemoving event.

game.Players.PlayerRemoving:Connect(function(player)
     local UserID = "Player_"..player.UserId
     local Cash = player.leaderstats.Cash
     DataStore:SetAsync(UserID,Cash)
end

A few new terms here. When you place two periods between two types of data, you are "concantenating" it, meaning to form them together into one string. The SetAsync function saves Player data into the Data store.

However, there's one problem. Saving player data is a risky process, and things like poor connection could break the script. So, to prevent this from happening, it is important to place a pcall function into the script. This would cause the script to skip the code should it fail and continue.

game.Players.PlayerRemoving:Connect(function(player)
     local UserID = "Player_"..player.UserId
     local Cash = player.leaderstats.Cash
     local Success, Failure = pcall(function()
            DataStore:SetAsync(UserID,Cash)
     end)
     if Success then
           print("Data successfully stored")
     else
           print("Failure")
     end
end)

Allow me to explain. "Success" is a boolean, and "Failure" is the error message should it fail. If the data store saves successfully, then Success will be set to true. It will meet the condition of the if statement below it, and print "Data successfully saved."

If the data save failed, whether it be to poor connectivity or whatever, Success will be turned to false, and it will not meet the condition of the if statement below. The warn() function is just like the print() function, only it prints in orange. So it would print the error message in orange should the data save fail.

Basic Debugging Techniques[]

When you make scripts you will inevitably come across bugs. These are errors in your code that gives an unintended result. The important thing is not to give up, since all bugs are, in some way, solvable. There are three main types of bugs.

Syntax Errors[]

Syntax errors are minor, usually easy-to-fix bugs that occur whenever a player accidentally makes typos in the script. In English, it'd be like saying "I am boy." It is wrong, because the article is missing. In Roblox Lua, it'd be like the script below. See if you can catch what's wrong in this script:

script.Parent.Touched:Connect(function(hit)
     print("Part Touched")
end

In this code, if you notice, the end tag has no closing bracket. These are important for events.

local Humanoid = hit.Parent.FindFirstChild("Humanoid")

Assuming that 'hit' was the parameter for a Touched event, this code checks whether or not the thing that touched the part was human. You will notice, however, that after 'Parent' there is a period. FindFirstChild is a function, therefore it should have a colon, not a period.

Semantic Errors[]

These are slightly harder to catch. These occur when the syntax is correct, but in context the code makes no sense. In English, it'd be like saying "I am a university at professor." Although it's grammatically correct, it still makes no sense. Here's an example that tries to turn Part1 invisible.

game.Workspace.Part1.Tranparency = "lol"

This does not work, because Transparency property is a float value, not a string. Also, Transparency is not correctly spelled.

Runtime Errors[]

These ones are harder to catch. This one occurs when the script makes perfect sense, but for some reason it still cannot perform the operation. The most famous example of a runtime error is this:

print(1 / 0)

It is mathematically impossible to divide 1 by 0, so the system cannot print it. Here is an example more specific to Roblox:

script.Parent.Touched:Connect(function()
    game.StarterGui.InventoryGui.Enabled = true
end)

At first glance, there are no errors that would appear. However, nothing will happen if you touch the part, and you will not receive an error message. This is because of FilteringEnabled, you cannot make the GUI appear on a server script. The game will simply refuse to perform the operation.

Automated Syntax Checks[]

More often than not, if you have a syntax/semantic error in your script, the system will automatically find it and the specific error will be highlighted in red or blue. Hovering over these words will provide a tooltip with a brief explanation.

So, if you were to type in this code:

local db =

The line would be underlined in red or blue, and hovering over the text would say this: Incomplete statement: expected assignment or function call. To fix the bug, you'd go and assign a value, or remove the equal sign.

The Output Window[]

Sometimes you just don't notice the error at first glance and enter the game without fixing it. So, you would get red, bolded text in the output window explaining the error. Clicking on the message will take you directly to the script with the error line highlighted.

Print Statements[]

Sometimes the Output window would show nothing, even though the script didn't run properly. Let's say I wanted to enter this code:

local IntValue = script.Parent.IntValue
if IntValue >= 1 then
    print("IntValue is greater than 1")
end)

The code would not print, even if IntValue was set higher than 1. Perhaps it was something in the first line that didn't run properly. Adding a print statement like this would test whether the first line worked:

local IntValue = script.Parent.IntValue
print(1)
if IntValue >= 1 then
    print("IntValue is greater than 1")
end)

Test the game. You could see "1" posted in the Output window, therefore line 1 worked without an error. That means that the error was in the next line. Using print statements helps narrow down which line has an error.

The Developer Hub[]

The developer hub is a depository of information on all things Roblox Studio. If you are unsure of what caused an error, try looking up the concepts in the developer hub.

Let's say I have no clue what parameters I need to include in a TweenService:Create() function. Then, I would look up the function to find the specific page, then find the parameters section. It would include the parameters, the data type of the parameters, and what it returns.

So, it is a very useful tool in finding solutions to your bugs.

The Devforum[]

But what if the hub turns out to be too technical for you to understand? Unlike the general forums, the developer forums exist to this day, for developers looking to solve their own and others' programming questions. If you come across a bug, chances are others will have come across that bug. Look up the error message, and look at forums to see if someone else has solved your problem for you.

Let's say you were wondering why you cannot see a TextLabel on a SurfaceGui through a window. Look it up, and you will find quickly that you can solve the problem simply by changing the material of the window and/or increasing the transparency.

The devforum isn't the only forum for scripting problems. For example, if you need to post and haven't yet earned the rank of Member yet, just go to scriptinghelpers.org, where you can post as long as you link your Roblox account.

Common Error Messages[]

Sometimes the error messages given in the Output window are extremely cryptic and undescriptive, so here's a quick guide that explains what some error messages mean.

Expected x, got y[]

This error message appears whenever you made a simple syntax error. This can happen if you use the wrong punctuation, or use an invalid data type in a statement. So if I were to type in this:

script.Parent.Position = false

The code would not work, because the Position property is a Vector3 or CFrame data type, but false is a boolean. So, the Output would print "Expected Vector3, got boolean." Fix this by replacing "false" with coordinates.

X is not a valid member of Y[]

This error occurs if the server could not find the object or property being accessed with the path. So, if I were to insert this script into a Model, it would not work:

script.Parent.Position = Vector3.new(1,4,4)

It will not work, because if you look in the Properties window, you will see that there is no such property that alters its position. You would have to script each part individually, or use a WeldConstraint.

This can also occur from spelling errors in your script.

Argument x missing or nil[]

This error occurs when you, in a function, omit a necessary parameter. If I were to insert this in the Workspace:

game.Workspace:FindFirstChild()

The code would not work, because I have yet to give the function something to look for. So, the code won't know what to look for, giving an error.

Infinite yield possible on x[]

This occurs when you use a yielding function that's supposed to find something that has not yet loaded. For example:

game.Workspace:WaitForChild("Wheel")

If Wheel does not exist, then the script would print the error in orange and will wait until it exists. It could end up waiting forever for it to load, so any code underneath it will not work.

Incomplete statement: expected assignment or function call[]

This occurs when part of a line is missing.

local Var =

You can see that I have not assigned Var a value, but I have included an equals sign. This would create an error. If you wish to set Var to nil, exclude the equals sign.

And that's all you need to know to make your first functional game! If you need any extra skills, go to the Expert Scripting Tutorial.

Examples[]

Kick Player Block[]

This tutorial will kick a player whenever he touches a part. Again, you cannot use local scripts in the Workspace, so use a RemoteEvent.

script.Parent.Touched:Connect(function(hit)
      local Humanoid = hit.Parent:FindFirstChild("Humanoid")
      if Humanoid then
           local player = game.Players:GetPlayerFromCharacter(hit.Parent)
           game.ReplicatedStorage.RemoteEvent:FireClient(player)
      end
end)

Then, create a listener in StarterGui using a local script.

game.ReplicatedStorage.OnClientEvent:Connect(function()
     game.Players.LocalPlayer:Kick("lol")
end)

Here, I am using a Kick() function to kick the player from the game. The LocalPlayer is the player that fired the event. In a Kick(x) function, parameter x represents the reason for the player's kick.

Grenade Spawner[]

This tutorial will demonstrate how to make a functional grenade appear and explode when a part is clicked.

local debounce = false
local GrenadeSpawner = script.Parent
GrenadeSpawner.ClickDetector.MouseClick:Connect(function()
    if debounce == false then
         debounce = true
         local Grenade = Instance.new("Part")
         Grenade.Size = Vector3.new(1,2,1)
         Grenade.Parent = game.Workspace
         Grenade.Position = Vector3.new(1,1,1)

          local TextureMesh = Instance.new("SpecialMesh")
          TextureMesh.Parent = Grenade
          TextureMesh.TextureId = "rbxassetid://290930193"
          TextureMesh.MeshId = "rbxassetid://294740282"

           wait(1)
          local Explode = Instance.new("Explosion")
          Explode.Parent = Grenade
          Grenade:Destroy()
          debounce = false
    end
end)

In this script, when GrenadeSpawner is clicked, it will create a part. Then, it will create a SpecialMesh, which will make it look like a real grenade. Of course, you will have to change the asset IDs.

Then, it will wait for 1 second before exploding and destroying the grenade. Debounce is only there to make sure only one grenade can exist at a time to prevent spamming.

Advertisement