Leaderboard Scripting Guide Help

Hi there, I’m hoping someone could help me decipher a roblox guide. :sweat_smile:

(For context: I’m new to scripting, I’ve dabbled before but I forgot pretty much everything after going on a long hiatus… oops)

I’m trying to create a leaderboard for an obby I’m creating and found this guide on the website: https://create.roblox.com/docs/players/leaderboards (I am renaming instances to my preference so I don’t know if that’s causing issues but I’m assuming that it shouldn’t)

For the most part I’ve done everything properly, the only thing is on step 3, it says “Inside the connected function, create a new Folder instance, name it leaderstats, and parent it to the player” I did create the folder and insert the script (without any changes) which was

local Players = game:GetService("Players")

local function leaderboardSetup(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
end

-- Connect the "leaderboardSetup()" function to the "PlayerAdded" event
Players.PlayerAdded:Connect(leaderboardSetup)

but I have no idea what the connected function is or how to parent it to the player, I’ve tried putting the folder in ServerScriptService, StarterGui, StarterPlayer, Workspace, Teams, I’ve put it under the Leaderboard script that’s in ServerScriptService, but none of those seem to be the “connected function”

I’m assuming that

leaderstats.Parent = player

Parents it to the player so it’s really more and issue with “connected function” but correct me if I’m wrong. Any help is appreciated. :slight_smile:

5 Likes

Hello!

That’s easy! In your script you created a function called leaderboardSetup. At the end of your script, you used a method known as :Connect(), which attaches a function to an event that fires when certain things happen in game.

For example, you used the Players.PlayerAdded event, which fires every time a player joins the game. So in your case, the leaderboardSetup function will create a new leaderboard for every player who joins.

You do exactly that: leaderstats.Parent = player.

You may not see it yet, as leaderboards in Roblox only appears whenever values are assigned to it. Basically, to use leaderstats, you will create a values inside the leaderstats folder that will keep track of the player’s money and/or stats.

If anything needs to be elaborated or explained again, don’t hesitate to ask! Hope this helped! :slight_smile:

4 Likes

I think I kind of get it but I’m still confused… so would the folder that I created (called leaderstats) just belong under workspace? I also don’t understand what you mean by create values in the folder, how could I do that?

2 Likes

The “connected function” refers to the function that you’ve connected to the PlayerAdded event. In your code, you’ve connected the leaderboardSetup function to the PlayerAdded event using:

Players.PlayerAdded:Connect(leaderboardSetup)

This line essentially says, “when a player is added to the game, execute the leaderboardSetup function.” So, when a player joins the game, the leaderboardSetup function is automatically called.

The player parameter in leaderboardSetup(player) represents the player who just joined the game. The line leaderstats.Parent = player is correctly parenting the “leaderstats” folder to the player.

Make sure your script is in a location where it will run when a player joins, such as in ServerScriptService. Also, ensure that there are no errors in the script. If you’ve done everything correctly, the “leaderstats” folder should be created for each player automatically when they join the game.

2 Likes

No, because in the script, you are parenting the folder to the player, so it shouldn’t be visible under workspace.

I’ll show you!

local Players = game:GetService("Players")

local function leaderboardSetup(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

local Money = Instance.new("NumberValue", leaderstats) --You can also sets the object's parent through the parameters
Money.Name = "Money"
Money.Value = 0 --Change this to the starting amount you want each player to get
end

-- Connect the "leaderboardSetup()" function to the "PlayerAdded" event
Players.PlayerAdded:Connect(leaderboardSetup)
2 Likes

Quick answer that might help you:
leaderstats don’t show up on the in-game leaderboard unless you have values parented to the folder:

local Players = game:GetService("Players")

local function leaderboardSetup(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

    local stat_1 = Instance.new("NumberValue")
    stat_1.Name = "Points"
    stat_1.Value = 1
    stat_1.Parent = leaderstats
end

-- Connect the "leaderboardSetup()" function to the "PlayerAdded" event
Players.PlayerAdded:Connect(leaderboardSetup)

^ this will cause your leaderboard to show the relevant leaderstats.

The in-depthy nitty gritty:

“Connected function” is referring to the function that is ‘passed’ (aka what you put in the parenthesis) when you call “Connect” on an event. In this case it’s referring to the “leaderboardSetup” function.

For this to make sense we have to understand a little bit about the Roblox engine.

An event is a member of a class. Events are actually their own DataType, acting as a “Script Signal”. You can think of them like sending out a signal when a specific thing or occurrence has taken place. In the example of your code, “Players” is the class and “.PlayerAdded” is the event.

Now, that’s great and all, but events are kinda useless unless we can actually do something with them. So how do we do that? By using the Connect method! When we call Connect on an event, it tells the game to run whatever code was Connected to the Event whenever the Event occurs.

This is where you are connecting the function (leaderboardSetup) to the event (.PlayerAdded)

Some events have “arguments” associated with them, which are basically slots for specific data associated with the event. For example: when a new player joins, and the PlayerAdded event fires, it would be helpful to be able to reference the newly joined player in our Connected function so we can, for example, load their saved data. Luckily, Luau does just that for us by passing an argument that acts a reference to the new player.

In the line below, the “player” inside the parenthesis is the argument, and refers to the new player that just joined.


This means that your assumption is correct:


Moving on:

To create a new value, we want to make a new object that stores the value (aka a “class” like from before). To do this we can create a NumberValue. This types of object is an “Instance” object, and can be made using the “.new” constructor of the Instance class. So in this case, we’d do: Instance.new(“NumberValue”)

In the code at the top of this post, you can see that both the Folder object named ‘leaderstats’ and the subsequent NumberValue object named ‘Points’ are created using Instance.new()

2 Likes

what should I do if the folder (and consequently the script) are visible in the workspace then?

Screenshot 2023-12-12 210908

2 Likes

This was super helpful, thank you. Could you explain what “stat_1” is exactly and why you use it? Is that something I could change?

2 Likes

The folder should not at all be in workspace. If it is, delete it. The script should be placed in ServerScriptService, so it runs on the server (meaning for everyone.)

Stat_1 is the name of the value you are displaying on the leaderboard. You can change it to whatever you want.

1 Like

Oh, okay thank you. Should I have the script in ServerScriptService without the folder then? I’m a little confused because the guide said it was supposed to be a folder, or am I misunderstanding? There’s already a script I have in ServerScriptService:

local Players = game:GetService("Players")

local function leaderboardSetup(player)

end

-- Connect the "leaderboardSetup()" function to the "PlayerAdded" event
Players.PlayerAdded:Connect(leaderboardSetup)

are the scripts supposed to be there together or only one?

1 Like

Just one script in ServerScriptService, no folder required. :grinning:

1 Like

It sort of worked? Now I’m spawning at random checkpoints (instead of the starting spawns) though and the value doesn’t change after I pass a checkpoint. Currently these are the scripts I’m using for the checkpoints:

the main checkpoint script is:

local spawn = script.Parent
spawn.Touched:connect(function(hit)
	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then
		local player = game.Players:GetPlayerFromCharacter(hit.Parent)
		local checkpointData = game.ServerStorage:FindFirstChild("CheckpointData")
		if not checkpointData then
			checkpointData = Instance.new("Model", game.ServerStorage)
			checkpointData.Name = "CheckpointData"
		end

		local checkpoint = checkpointData:FindFirstChild(tostring(player.userId))
		if not checkpoint then
			checkpoint = Instance.new("ObjectValue", checkpointData)
			checkpoint.Name = tostring(player.userId)

			player.CharacterAdded:connect(function(character)
				wait()
				character:WaitForChild("HumanoidRootPart").CFrame = game.ServerStorage.CheckpointData[tostring(player.userId)].Value.CFrame + Vector3.new(0, 4, 0)
			end)
		end

		checkpoint.Value = spawn
	end
end)

and the script that’s supposed to add to the leaderboard value is:

local Players = game:GetService("Players")

local Checkpoint1 = script.Parent

local function onPartTouch(otherPart)
	local partParent = otherPart.Parent
	local player = Players:GetPlayerFromCharacter(partParent)
	local leaderstats = player and player:FindFirstChild("leaderstats")
	local level = leaderstats and leaderstats:FindFirstChild("level")

		-- Update the player's leaderboard stat
		level.Value += 1
	end
end

Checkpoint1.Touched:Connect(onPartTouch)

Also, how do I change the name from ‘points’ to ‘level’?
Screenshot 2023-12-12 213825

1 Like

You can just change “Points” in this line to “Level” (caps-sensitive)

2 Likes

Oh I didn’t see that, thank you!

2 Likes

Okay update:
I fixed the leaderboard not updating by adding a floating star above each checkpoint with the following script:

local Players = game:GetService("Players")

local Checkpoint = script.Parent

local function onPartTouch(otherPart)
	local partParent = otherPart.Parent
	local player = Players:GetPlayerFromCharacter(partParent)
	local leaderstats = player and player:FindFirstChild("leaderstats")
	local LevelStat = leaderstats and leaderstats:FindFirstChild("Level")

	if LevelStat then
		-- Destroy the pickup
		Checkpoint:Destroy()

		-- Update the player's leaderboard stat
		LevelStat.Value += 1
	end
end

Checkpoint.Touched:Connect(onPartTouch)

the ONLY issue now is that when I test, I spawn at random checkpoints instead of the start spawn. The script I have at each checkpoint (that I guess is supposed to fix this?) is:

local spawn = script.Parent
spawn.Touched:connect(function(hit)
	if hit and hit.Parent and hit.Parent:FindFirstChild("Humanoid") then
		local player = game.Players:GetPlayerFromCharacter(hit.Parent)
		local checkpointData = game.ServerStorage:FindFirstChild("CheckpointData")
		if not checkpointData then
			checkpointData = Instance.new("Model", game.ServerStorage)
			checkpointData.Name = "CheckpointData"
		end

		local checkpoint = checkpointData:FindFirstChild(tostring(player.userId))
		if not checkpoint then
			checkpoint = Instance.new("ObjectValue", checkpointData)
			checkpoint.Name = tostring(player.userId)

			player.CharacterAdded:connect(function(character)
				wait()
				character:WaitForChild("HumanoidRootPart").CFrame = game.ServerStorage.CheckpointData[tostring(player.userId)].Value.CFrame + Vector3.new(0, 4, 0)
			end)
		end

		checkpoint.Value = spawn
	end
end)

ServerScriptService has the leaderboard script which is:

local Players = game:GetService("Players")

local function leaderboardSetup(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local level = Instance.new("NumberValue")
	level.Name = "Level"
	level.Value = 0
	level.Parent = leaderstats
end

-- Connect the "leaderboardSetup()" function to the "PlayerAdded" event
Players.PlayerAdded:Connect(leaderboardSetup)

The start spawns have no scripts. I’m assuming the random spawn points has something to do with the leaderboard script because I only had the issue after I added it in, but I don’t know what to change about it to get rid of that issue.

Also, to everyone who’s helped so far: thank you so much it truly means a lot to me the patience and time given to explain this to me.

1 Like

Just wanted to commend your dedication / effort - it goes a long way in the field of coding and troubleshooting.

Anyway, long post but lots of explaining, hopefully it gets you squared away :slight_smile:

Part 1 - Intro / Goals to accomplish

So, looks like the goal here is to have a system that updates our Level value as we progress through the game, acting as a checkpoint system. General outline of what we want to do would be something like:

  1. Player joins →
  2. Setup checkpoint system →
  3. (Re)spawn their character at the correct place

We also want to make it so when they reach a new checkpoint in game, their checkpoint value updates.

To do this we need to keep track of:

  1. When a player joins
    We can do this using the PlayerAdded event.

  2. When a player spawns / respawns
    We can do this using the .CharacterAdded event.

  3. What a players checkpoint value is
    This can be accomplished by storing a value associated with each player, and that value should correlate to a spawn location.

  • We also need to come up with a system of how to update a players checkpoint value. This system is going to be dependent on how we store and read the values in the first place.

Having looked over your code, it looks like you’ve already got most of this done (nice!), it’s just a matter of making sure we have things implemented correctly and then we can resolve any errors we encounter.

Part 2 - Setup

From what I can tell, your current system works like this:

  1. Player joins → setup leaderstats.

  2. When a checkpoint is touched:
    2.1 Check for the players level stat. If it’s there, increase it by 1, and destroy the checkpoint (which also happens to be the scripts parent, thereby destroying our script as well)

    2.2 Check for the players checkpoint data. If it’s not there, then create it. Once it is there, then we:
    1) Set the checkpoint value to be the spawn object,
    2) Create a new .CharacterAdded connection that spawns our character at the the new checkpoint we just reached

This is a good start, but there’s a few things we should do to improve upon it so it works as intended:

  1. There’s two different scripts that are both running a Touched event on checkpoints with the goal of updating player data. We can combine these into one script with just one Touched event so there’s less stuff for the game to load / handle (i.e. game will run faster), and it keeps all our relevant code together in one spot, making it easier to work with in the future.

    1.1 We also never check to see if the Checkpoint we are touching is for a new area that we haven’t been to. Because of this, we could theoretically go back to the last checkpoint, touch it, and still cause our Level stat to increse by 1 even though we actually went backwards in the levels rather than forwards. I do see that you destroy the checkpoint when it’s touched, however this does come with the caveat that it will be destroyed for all players, meaning a checkpoint can only be used once. It should also be noted that calling Destroy() does more than just deleting the object, and will likely cause your script to stop running given it’s a child of the object you calling Destroy on.

    There’s a few ways we can solve this dilemma, but some options may be better than others. For now, lets assign a specific level to each checkpoint by setting it’s Name to be the same as its level. It’s not a perfect system, but it will work for what we need right now. We also want to implement a debounce system to help cut down on lag.

  2. We’re handling when a Player joins and when their character spawns/respawns in two separate scripts. Initially, this might seem like the logical way to do it since these two events seem independent of one another, but it’s actually not. I’ve put a full explanation as to why at the end of this post under ‘handling connections’ but for now lets focus on the general idea: We can detect when a player spawns through the .CharacterAdded event, however, this is a member of the “Player” object, so in order to use it, we have to reference the Character’s player somehow. We also need to do this for all Players in the game from the moment they join the game. How do we do that? By using the PlayerAdded event. You’ve already started that part, we just need to merge the two scripts.

    2.1 It should also be noted that in your checkpoint script, you make a new CharacterAdded connection every time a player changes checkpoints, without removing the old connections. We only need one CharacterAdded event per player, and we can tailor the instructions we give when CharacterAdded runs.

  3. You have the option of either manually adjusting a players spawn point every time they spawn via the CharacterAdded event – OR – you can utilize the Player.RespawnLocation property to specify which SpawnLocation they should spawn at. The linked page has some good information on the topic and should be able to help you make your decision.

There are some other tweaks we could make, such as handling all the Touched events for all the checkpoints in one script, rather than making the game load the same script for every checkpoint, but lets keep it simple for now.

Part 3: Implementing

So, what we need now is:

  1. A script that handles when we Join, sets up our playerdata and handles when we spawn
  2. A script that handles when we make it to a new checkpoint

There’s an optional second argument when using Instance.new that lets you specify the objects parent, and saves you the hassle of writing out the full thing every time. I also used an anonymous function when writing the CharacterAdded event. This is basically the same thing, except instead of writing out my function beforehand and calling it, I write the function inside the parenthesis.

local Players = game:GetService("Players")
local Server_Storage = game:GetService("ServerStorage")

local function handle_New_Players(player)
-- Setup the player's leaderstats and checkpoint data
	local leaderstats = Instance.new("Folder",player) 
	leaderstats.Name = "leaderstats"

	local level = Instance.new("NumberValue",leaderstats)
	level.Name = "Level"
	level.Value = 0

	checkpoint_Value = Instance.new("ObjectValue",Server_Storage:WaitForChild("CheckpointData"))
	checkpoint_Value.Name = tostring(player.UserId)
	checkpoint_Value.Value = workspace.Checkpoints:WaitForChild("Start") -- Assuming you have a folder/model of the checkpoints and name the first one "Start", you can change names as needed
	
-- Handle when a player spawns
	player.CharacterAdded:Connect(function(character) -- Anonymous function
		character:PivotTo(checkpoint_Value.Value.CFrame * CFrame.new(0,5,0))
	end)
-- Note: This wont work the first time we spawn after joining since our character tends to load before the game sets up this connection. 
-- We can respawn our character immediately afterwards to solve this.
	player:LoadCharacter()
end

-- Connect the handle_New_Player function to the event
Players.PlayerAdded:Connect(handle_New_Players)

Great, now we have a script that sets up our leaderstats and checkpoint data when we join, and will place us at the right spot when we spawn.

local Players = game:GetService("Players")
local Server_Storage = game:GetService("ServerStorage")

local checkpoint = script.Parent
local function onTouched(hit)
	local touched_Player = Players:GetPlayerFromCharacter(hit.Parent)
	if touched_Player and (not hit.Parent:FindFirstChild("Checkpoint_Debounce") then 
		touched_Player.leaderstats.Value = tonumber(checkpoint.Name)
		Server_Storage.CheckpointData:FindFirstChild(tostring(touched_Player.UserId)).Value = checkpoint
	end
end

checkpoint.Touched:Connect(onTouched)

And here’s our script that will update our leaderstats and checkpoint when touched, based on the name of the checkpoint.

Welp, that’s about it. As mentioned earlier, there are certainly numerous improvements we can make to the system and various ways to accomplish the same or similar goals, but as someone famously once wrote: that is left to the reader as an exercise

Bonus: Handling Connections

When we connect an event to a function, we’re actually making an object.

The server (aka ‘The Game’) has to track and handle these objects just like it does for all the scripts, folders, models, etc. That is to say, when we make connections, we increase the amount of memory we are using. If we run out of memory / try to use more than we have, our game crashes. By association, the less memory we use, the less the game has to worry about keeping track of, and the better (faster) our game will tend to run.

So, we want to make sure that all the connections we make are needed, and that we get rid of them once they are no longer needed. You might be thinking “How do I get rid of a connection??” Well, remember how Connections are actually just objects? those objects come with Methods - which are “built in functions” for an object. In this case we can use the Disconnect method that comes with our connection object to get rid of it! I found this to be a bit confusing when first learning, so here’s an example:

local connection_Object = game.Players.PlayerAdded:Connect(example_Function)
connection_Object:Disconnect()

When we call Connect, we can store the resultant connection object as a variable and then use that to call Disconnect if we need to. Additionally, as noted above, calling Destroy() on an object will disconnect all of an objects connections.

So, In the code you posted, we would be making a new connection to CharacterAdded every time a player touches a checkpoint, which adds up quick and will result in lag. By establishing only one of these connections when the player joins, we save memory and only give the game one set of instructions upon spawning. If we really wanted to optimize the checkpoint system, we could attempt to implement it such that we only call the connected function one time when we touch a checkpoint and subsequently disconnect the Touched event until we need to connect it again. You could also do this on the client that way each players ability to use checkpoints is independent of one another.

1 Like

I only got the chance to properly read through this now. For the most part, it’s been very helpful in explaining the details behind scripts and what each thing does and I appreciate you taking the time to write it all out. I decided to use the Player.RespawnLocation property as you mentioned and I used the following sample code under each checkpoint to achieve that:

local Players = game:GetService("Players")

local function addSpawn(spawnLocation)
	-- listen for the spawn being touched
	spawnLocation.Touched:Connect(function(hit)
		local character = hit:FindFirstAncestorOfClass("Model")
		if character then
			local player = Players:GetPlayerFromCharacter(character)
			if player and player.RespawnLocation ~= spawnLocation then
				local humanoid = character:FindFirstChildOfClass("Humanoid")
				-- make sure the character isn't dead
				if humanoid and humanoid:GetState() ~= Enum.HumanoidStateType.Dead then
					print("spawn set")
					player.RespawnLocation = spawnLocation
				end
			end
		end
	end)
end

local firstSpawn

-- look through the workspace for spawns
for _, descendant in pairs(workspace:GetDescendants()) do
	if descendant:IsA("SpawnLocation") then
		if descendant.Name == "FirstSpawn" then -- i change this according to the name of the spawn
			firstSpawn = descendant
		end
		addSpawn(descendant)
	end
end

local function playerAdded(player)
	player.RespawnLocation = firstSpawn
end

-- listen for new players
Players.PlayerAdded:Connect(playerAdded)

-- go through existing players
for _, player in pairs(Players:GetPlayers()) do
	playerAdded(player)
end

Although the checkpoint system is essentially fixed, the leaderboard isn’t showing up when the character finishes loading and I’m not sure how to solve that or what’s interfering. I tried to use to scripts you provided (thank you) and I was able to understand for the most part what each part does, I just don’t understand where I should put the scripts and how I should use them(?) Under part 3, you use anonymous function in the script, I clicked on the hyperlink and I’m still quite confused as to how that works or what it means. I understand most of that script, the idea of anonymous function is really throwing me off though. I was also wondering, under 1.1 you mention a debounce system, what exactly does that do/mean as well?

Here’s your script with leaderstats added in:

local Players = game:GetService("Players")

local function addSpawn(spawnLocation:SpawnLocation)
	-- listen for the spawn being touched
	spawnLocation.Touched:Connect(function(hit)
		local character = hit:FindFirstAncestorOfClass("Model")
		local plr = Players:GetPlayerFromCharacter(character)
		if plr then
			if plr and plr.RespawnLocation ~= spawnLocation then
				local humanoid = character:FindFirstChildOfClass("Humanoid")
				-- make sure the character isn't dead
				if humanoid and humanoid:GetState() ~= Enum.HumanoidStateType.Dead then
					print("spawn set")
					plr.RespawnLocation = spawnLocation
					local new_Level = spawnLocation:GetAttribute("Level") -- Gets the value of the Level attribute
					if new_Level then -- Check if theres a new level value
						plr.leaderstats.Level.Value = new_Level -- Update the leaderstats value we created in the PlayerAdded function
					end
				end
			end
		end
	end)
end

local firstSpawn

-- look through the workspace for spawns
for _, descendant in pairs(workspace:GetDescendants()) do
	if descendant:IsA("SpawnLocation") then
		if descendant.Name == "FirstSpawn" then -- i change this according to the name of the spawn
			firstSpawn = descendant
		end
		addSpawn(descendant)
	end
end

local function playerAdded(player:Player)
	player.RespawnLocation = firstSpawn
	-- Add stats to player
	local leaderstats = Instance.new("Folder",player) leaderstats.Name = "leaderstats"
	local checkpoint_Stat = Instance.new("NumberValue",leaderstats) checkpoint_Stat.Name = "Level" -- Can change the name to whatever you want the stat to be called
	checkpoint_Stat.Value = 1 -- Assuming there is no "Level 0" and your starting on "Level 1"; can change as you need 
end
Players.PlayerAdded:Connect(playerAdded) -- Listen for new players

To make the leaderstats work, all you need to do is add a NumberValue attribute to each spawn; which you can do by:

  1. Selecting the object
  2. In the properties window for the object, scroll all the way to the bottom where it has the sections for Tags and Attributes.
  3. Click on the little + icon under the Attributes section:
    image
  4. Name the attribute “Level” and make its type a NumberValue.
  5. Set the Value to be whatever number level that SpawnPoint is supposed to be.

To make it easier; here’s a script that will automatically add this exact attribute to each spawn you have that way you can just set the values:

for i, v in pairs(workspace:GetDescendants()) do
	if v:IsA("SpawnLocation") then
		v:SetAttribute("Level",0)
	end
end

Regarding the anonymous functions; they’re just a way of connecting an event to a function without having already created the function you want to connect; for example:

-- Named function (aka not anonymous):
local function on_Touched(part_That_Touched)
	print (part_That_Touched.Name)
end
workspace.Part.Touched:Connect(on_Touched)


-- Anonymous function:
workspace.Part.Touched:Connect(function(part_That_Touched)
	print (part_That_Touched.Name)
end)

In the named function, we put the name of the function in the parenthesis after calling Connect. The argument part_That_Touched is automatically passed to the on_Touched function by Roblox.

In the anonymous function, we don’t call a pre-existing function but instead write out the function we want to call inside the parenthesis. Because we aren’t passing an existing function to Connect, we can’t call the function we’ve just made outside of the use-case we created it for (in this case its only able to be called when a .Touched event occurs for this specific part). Hence, its anonymous because it doesn’t have a name or clear means of calling it like our on_Touched function from before. However, we still establish our arguments just like we did before: by putting the part_That_Touched argument in the parenthesis of our function. The only difference is that now our function is located inside the Connect() parenthesis. This is also why there is a ) after the end, because we have to make sure we close our parenthesis.

for the leaderstats script would I put one of those under each spawn or one for each spawn in serverscriptservice, or would I only put one in serverscriptservice (if so how would that affect the decendant name if they each have different names?)

I tried to add NumberValue attributes to each of the spawns but there’s no NumberValue type option
image
image
I didn’t know where to put the script you provided so I preferred to try doing it manually

Also thank you for further explaining anonymous functions to me, I think I get the general idea of them.

There’s no such thing as a ‘NumberValue’ attribute because a NumberValue is an instance that has a property called Value that only stores numbers (not to be confused with integers which is stored by IntValue, another instance that shares a common base object called ValueBase), but its equal is the number attribute.

1 Like