How to make a Skill Tree in Roblox Studio (+Graph Theory) — Transcript
Full transcript
- 0:00Skill trees are a part of a lot of games these days,
- 0:03and they may seem intimidating to make in your own games.
- 0:07But in this video, I will show you how to make a skill tree
- 0:10where you can put different skills in a chain of unlocks
- 0:15in which you can unlock them
- 0:17and eventually get those crazy skills at the end of the day.
- 0:21So I’ll be showing you a lot of server-client interaction,
- 0:25the basics of graph theory –
- 0:26because skill trees are little graphs,
- 0:29and graphs are really important
- 0:30for computer science down the line
- 0:32if you want to get involved in it –
- 0:34and just some, like, GUI-interaction stuff as well.
- 0:37This is a pretty advanced video, and it’s a pretty long video,
- 0:40but trust me, you can get through it,
- 0:43and it’ll be really cool at the end
- 0:45to see your finalized skill tree
- 0:47that you can put in your own game.
- 0:49This is a really important video.
- 0:51It covers a bunch of different concepts
- 0:53that I think are pretty important to your development career
- 0:55and career as a computer scientist,
- 0:57so I would recommend that you watch the entire thing
- 1:01and maybe learn something from it.
- 1:03And if you do, make sure to like the video,
- 1:05subscribe to my channel for more
- 1:06advanced Roblox scripting videos like this,
- 1:09and without further ado, let’s get started.
- 1:13Captions by rc8s
- 1:17A skill tree in its most basic form is a graph,
- 1:21and a graph is just a set of nodes, or vertices –
- 1:26they can be called a bunch of different things –
- 1:28connected by some edges.
- 1:31So in our case, in our skill tree,
- 1:33these edges are going to be directed.
- 1:35And directed means that there’s going to be, like, an arrow
- 1:38going from one node to another.
- 1:41So this can be an example of a graph.
- 1:45We can label it like “A”...
- 1:49“B,” and “C.”
- 1:51And so, thinking of a skill tree like a graph
- 1:54gives us a lot of different tools to help us reason about it,
- 1:59because, for one, when you think about –
- 2:02normally, like, this is a pretty easy relationship to talk about.
- 2:05This is like – you know – A leads to B, B leads to C,
- 2:07and that makes sense.
- 2:08But what happens when you have something like D...
- 2:14...and then maybe, like, E down here?
- 2:18And let’s say that A points to E...
- 2:24...and then E points to D but C also points to D.
- 2:28And this relationship is a little harder to reason about.
- 2:31But luckily, by using graphs,
- 2:32it makes everything pretty trivial.
- 2:35So our plan for this skill tree is to create a graph
- 2:40where, basically, each of the skills is our nodes
- 2:42and each of the edges is like the prerequisites to unlock it.
- 2:46So, for example, to unlock skill D,
- 2:48you’re going to need both E and C,
- 2:52and these arrows kind of tell you what these are.
- 2:54So, now let’s go into the code
- 2:56and kind of graph this whole idea out.
- 2:59So now I’m in Roblox Studio with a project set up.
- 3:02Now, the only important thing you need to note
- 3:04is that I already have a skill-tree "gooey" [GUI]
- 3:06in my project with a skill tree here.
- 3:12Let me make it visible.
- 3:13This is a – just a picture. It’s an image label.
- 3:17And there’s a couple other aspects to it
- 3:19that I will talk about a little bit later.
- 3:21But basically, this is like an elemental skill tree.
- 3:24So you have, like, the base. This is the base root of the tree.
- 3:28And then you got water and fire, and water points to ice
- 3:31and water also points to steam.
- 3:33I know – this is a horribly drawn steam icon.
- 3:36I did it myself. That’s the issue, but don’t mind it.
- 3:38And then steam leads to smoke.
- 3:41Now, this relationship is kind of contrived.
- 3:44Again, on an actual skill tree,
- 3:46you’d probably want something a little bit smarter, but,
- 3:48for demonstration purposes, this should work.
- 3:51Now, I was thinking about making a script to procedurally generate
- 3:54this kind of image, but upon my research,
- 3:57most skill trees seem to be hand drawn,
- 4:00and I think that’s kind of, like, how most people
- 4:02would prefer it to happen, so I’m gonna leave it like that.
- 4:04But if you guys would like to see a way
- 4:06to procedurally generate this sort of skill tree,
- 4:08then let me know,
- 4:09but it definitely deserves its own video.
- 4:11But for now, make yourself
- 4:13some sort of image or whatever to represent your skill tree
- 4:16and make the relationships.
- 4:18Then we can get going.
- 4:20So going back to ServerScriptService,
- 4:25we’re gonna make a module script,
- 4:27and this is going to be our SkillTreeInfo.
- 4:32So, this module script, I’m gonna delete everything inside of it.
- 4:35What it’s basically going to be is just kind of like a reference
- 4:37to the information pertaining to our skill tree.
- 4:40So all we’re gonna do is we’re gonna return a table,
- 4:43and this table’s going to contain
- 4:45all of, like, the stuff that we need for our tree.
- 4:48So, the first one I’m going to do is Fire.
- 4:50So Fire I’m just gonna say costs three points – so Points = 3 –
- 4:55and then the prerequisites.
- 4:57So Prereqs, meaning the things that you have to unlock
- 5:01before getting it.
- 5:03So if you’ve ever taken, like, classes
- 5:05that require prerequisites, this is what I’m referring to.
- 5:08So, this will have no prereqs.
- 5:10So each of our skills is going to have a Points value,
- 5:13which is how many skill points it’ll take to unlock,
- 5:16and we’ll implement that in just a second – like, skill points –
- 5:19and then it’ll also have a list of prereqs.
- 5:22So this is Fire, and then...
- 5:25So we can see Fire takes nothing,
- 5:27because this is going to be unlocked by default.
- 5:29We’re just going to assume this is unlocked by default,
- 5:30and then we need to graph out the rest of these.
- 5:32Now, this is kind of a verbose process,
- 5:35so I’m gonna go yoink my other – my stuff from another screen
- 5:41and put it on here.
- 5:43So this is all you need,
- 5:46and I will have all the code in the description
- 5:49so you guys can check it out if need be.
- 5:51But when you make a...
- 5:55skill with a prerequisite inside of the list,
- 5:59you put a string with the name.
- 6:01Make sure this name matches
- 6:03the name of the actual entry exactly.
- 6:07So this “Water” matches this “Water” up here.
- 6:09This “Steam” matches this “Steam” up here.
- 6:11So this is how you’ll define your skill tree.
- 6:13And we’re doing this in a separate file
- 6:14so that maybe someone who doesn’t actually code,
- 6:17who’s like a game balancer, can tweak these values
- 6:20as they see fit.
- 6:21But keep in mind that, like, if you change the prereqs,
- 6:24you’re probably gonna change the actual image for the skill tree.
- 6:27But it’s good to keep this in a separate file.
- 6:29This almost acts like a JSON file,
- 6:30if you’ve done web development before,
- 6:32and it looks pretty similar to it.
- 6:34So this is all done.
- 6:35Also, note that the trailing comma
- 6:36after this doesn’t actually matter.
- 6:39You could have it, you could not have it. It's up to you.
- 6:41Really, you should have it; it's better practice.
- 6:43But at the end of the day, it don't really matter.
- 6:46Okay, so let’s go to our ServerScriptService again,
- 6:49and we can make a script, and I guess
- 6:51we can name this script just SkillTree.
- 6:55What this will have is –
- 6:57first we’re going to start by setting up our player,
- 7:00because every player when they join our game
- 7:02is going to have a set amount of skill points,
- 7:07and you can obviously tweak this to your game
- 7:09as you see fit, but for this example,
- 7:11as I want a complete example
- 7:12that actually works out of the box,
- 7:14we’re gonna set it up ourselves.
- 7:15So, first things first, we’re actually gonna have to get
- 7:18our SkillTreeInfo module script by saying require...
- 7:25So – and we also need to say...
- 7:31There we go. Because when each player joins,
- 7:34we’re gonna set up their leaderstats
- 7:37to make the skill points.
- 7:41So this is a basic, you know, pattern in Roblox where we...
- 7:46hook our PlayerAdded to a new function,
- 7:49and in this function, we’re gonna say...
- 7:57And then leaderstats.Name
- 7:59is going to be equal to leaderstats.
- 8:03And then leaderstats.Parent is going to be equal to player.
- 8:08So what this will do
- 8:09is this will make a leaderstats folder in our player,
- 8:11and that means any value that we add to this
- 8:13will appear in the leaderboard on the top right,
- 8:15and I’ll show you that in just a second.
- 8:17But for now, let’s create our skill points by saying...
- 8:23...and then we’ll say...
- 8:29Just name it whatever.
- 8:30And we’re gonna do skillPoints.Value.
- 8:34And then for the value, like, in an actual game,
- 8:37you’d set it as 0 and you’d get skill points through, like,
- 8:39killing enemies or whatnot.
- 8:41But for us, we’re gonna set it to 100
- 8:42because I want skill points to start out
- 8:44so I can actually spend them,
- 8:45but keep that in mind for your own game.
- 8:47We’re also gonna set skillPoints.Parent
- 8:49equal to leaderstats.
- 8:51And then there we go.
- 8:53So we have the leaderstats set up.
- 8:55Let’s just double-check that they work.
- 8:59Oh. Well, they don’t work, because we required
- 9:01with invalid arguments. Um...
- 9:04It’s “SkillTreeInfo” up here. Don’t get it wrong,
- 9:07because the SkillTreeInfo is a module script,
- 9:09the skill tree is our actual script.
- 9:11So let’s run it again.
- 9:14We have 100 skill points in the top right.
- 9:16That’s why we put in leaderstats. Okay, we have that.
- 9:19But also, in addition to our skill points,
- 9:21we’re gonna need something to actually store the skills.
- 9:24So I’m going to be storing the skills in a...
- 9:28So each player is going to have their skills stored
- 9:29in a global table.
- 9:32It’s gonna be called playerSkills equals a blank table.
- 9:35Now, in an actual game,
- 9:37you would like this to be some sort of data store
- 9:40or, like, connected to your data store somehow
- 9:42so it saves between sessions, but for us,
- 9:44that’s outside the scope of this video.
- 9:46If you want to see how to hook it up to a data store,
- 9:48just let me know.
- 9:49I also have a data-store video that you can check out,
- 9:51although it might be a little outdated at this point.
- 9:53But there are good resources that are on the Forum
- 9:55that I also put in the description
- 9:57for you to use. But we can say...
- 10:03...is equal to a blank list.
- 10:05So what this does is, every time a player joins the game,
- 10:07it initializes their skills to a blank list.
- 10:12And this means later down the line
- 10:14when we’re trying to add skills to the player –
- 10:17and the skills will be in the form of just like
- 10:19a list of strings – we can just do that.
- 10:22Also, if you plan to change the name of your skills,
- 10:26I would recommend also having, like, an ID in here
- 10:29which is set to a number that you can change down the line.
- 10:32But for us, since this is small and we know what our skills are,
- 10:35we’re just gonna leave it as a name.
- 10:36It just makes the code a little simpler,
- 10:38but it should be relatively easy to adapt
- 10:41if you do that.
- 10:43Okay, so now we have the setup done.
- 10:45We now – let’s start making,
- 10:47like, the interaction between the server and client,
- 10:49and the first thing we’re going to do
- 10:51is create a remote function in ReplicatedStorage
- 10:54that’s going to be called GetSkillTreeInfo.
- 10:58So what this is going to do
- 10:59is it’s gonna get each player’s individual skill-tree info.
- 11:02Because keep in mind, if you have unlocked a skill,
- 11:05there’s really nothing to do on the client.
- 11:07The client doesn’t need that extra information.
- 11:09So we’re gonna first start by making –
- 11:12or I guess first we need to get our SkillTreeInfo reference,
- 11:15so we’re gonna say...
- 11:24There we go.
- 11:25So, since this is a remote function, we need –
- 11:27the client’s going to be wanting to retrieve a value.
- 11:30That’s why we use a remote function. So we’re gonna say...
- 11:35...is going to equal a function with the player.
- 11:39So notice how this is OnServerInvokeequals,
- 11:42because this is a remote function, not a remote event,
- 11:45because we want to return a value.
- 11:47That’s the reason we’re using a remote function.
- 11:50So, inside of here,
- 11:52we want to first get the player’s skills.
- 11:54So the player’s currently owned skills
- 11:57are going to be equal to playerSkills[player.UserId].
- 12:01Because, like, this table is going to have
- 12:03a list of skills for each player keyed by their user ID,
- 12:07and below that, we’re going to say...
- 12:12...is going to be equal to nothing.
- 12:14This is what we’re gonna populate,
- 12:16to fill, with all of the remaining skills
- 12:17that the player needs to unlock and their prerequisites.
- 12:20So we’ll basically be copying, selectively copying,
- 12:22the stuff from this list that they need,
- 12:25because we don’t need to send them extra information.
- 12:28So we’re going to say...
- 12:35Notice how we don’t need to do pairs or ipairs. That's old now.
- 12:39Luau allows you to do this, which is pretty cool.
- 12:42We can say...
- 12:50...then we can say...
- 12:56So what we’re doing here is, if we don’t find the...
- 13:00like, for example, we don’t define Fire
- 13:02in the list of player skills,
- 13:05then we can add it to their own skill info
- 13:08keyed by the name, and the info is just copying this stuff here.
- 13:12Because this is the name, this is the info.
- 13:14You can copy that there to them in this new table,
- 13:16and then we’re just going to return this new table.
- 13:27There we go.
- 13:29So then, on the client side,
- 13:31we’re gonna want to create a new script,
- 13:36or a new local script,
- 13:38and we’re gonna call this...
- 13:44...LocalSkillTree.
- 13:48And then, in here,
- 13:50we’re going to get a couple of things.
- 13:52So the way I have my setup is,
- 13:56we have a skill-tree- toggle button,
- 13:58which is parented directly to the GUI,
- 14:00we have a skill-tree image label, which has the image in it,
- 14:06then we have an unlock button parented to that,
- 14:08and then inside of this, we have the...
- 14:13uh, folder of NodeSelectors.
- 14:16These are buttons that are on top of the actual thing.
- 14:21So when I made them visible, see how they dim out the skills?
- 14:25This will kind of be like the visual indicator to the player
- 14:27that, like – “This skill is not unlocked. You need to unlock it.”
- 14:30So my goal here is, like,
- 14:31when you click on the dimmed-out button,
- 14:34it’s gonna change the text of this unlock button down here,
- 14:37and when you unlock it, it’s just gonna make it invisible.
- 14:40So that should be what we want to do.
- 14:42And I also have the GUI RatioAspect...
- 14:44AspectRatioConstraint for the image label
- 14:48because – you know, so it scales.
- 14:50Um, it’s not perfect, because, uh, this –
- 14:55each of these buttons has a UICorner,
- 14:57so if you see, it’s not a true circle,
- 14:59but I’m gonna be honest – I don’t really care that much.
- 15:02Have an almost-circle is good enough.
- 15:04I like the effect. It’s good enough.
- 15:06So the goal here is,
- 15:08depending on the information we get from the server,
- 15:10we want to return, or we want to edit,
- 15:14which ones are visible, which ones are invisible,
- 15:17based on which ones are unlocked and then also change the way –
- 15:21or, like, what the buttons do.
- 15:23Because when this goes invisible, we want to make the button inert
- 15:28so it doesn’t actually do anything.
- 15:29So let’s get this in code so you can stop hearing me yapping
- 15:32and start seeing actual code be put to use.
- 15:35So we’re also gonna make
- 15:36this skill tree thing invisible by default
- 15:39because we want to click on this button to unlock it.
- 15:41So we’re going to start by getting the frame,
- 15:46so the skillTreeFrame, which is equal to...
- 15:54And then we’re gonna say...
- 15:59Actually, it’s not really a frame.
- 16:01It’s probably an image label.
- 16:02Whatever. It’s – same thing. It’s – I can call it a frame.
- 16:05So we’re gonna say local nodeSelectors...
- 16:13Actually, no. Um...
- 16:18...and then...
- 16:25...and then the skillTreeToggle will be equal to...
- 16:37So just to be safe,
- 16:38we’re gonna change the skillTreeFrame
- 16:41to be a WaitForChild().
- 16:43Not sure if it’s needed,
- 16:45but we’re going to do it just in case.
- 16:47And then we’re also going to get the GetSkillTreeInfo –
- 16:53oh, my God, this is a mouthful –
- 16:54uh, function from ReplicatedStorage. So...
- 17:00There we go.
- 17:01So we got all of the stuff that we need for our client,
- 17:05because we’re gonna do the unlock button later.
- 17:07NodeSelectors is just a folder of all of the buttons
- 17:12that are, like, the little gray pieces.
- 17:14So I guess – let me make it a little more clear,
- 17:16if you’re not sure what I’m talking about.
- 17:17The node selectors are just the gray portion,
- 17:20like, the semi-visible portion, on top of the GUI.
- 17:22And again, this stuff will be in the description
- 17:24so you can play around with it.
- 17:26I know it’s a lot,
- 17:27but I want to take it to the actual coding content.
- 17:29That’s more important. The GUI does take a while to make.
- 17:32It’s kind of annoying, but, like, it’s pretty simple.
- 17:35So we’re gonna start by defining the local playerSkillTreeInfo,
- 17:40which is gonna be the table that we’re gonna get from the server,
- 17:44and then we’re gonna define our current selection,
- 17:49which is gonna be equal to nil
- 17:51depending on, like, which skill we have selected.
- 17:53So you’re gonna select the skill,
- 17:54and the unlock button will reflect how much it costs
- 17:57and what skill it is.
- 17:58And then now, we’re going to say...
- 18:07Connect a function.
- 18:09And here, this is going to be where we flick on and off
- 18:12the GUI for the skill-tree stuff.
- 18:14We click this and the, like,
- 18:15menu appears. And so we’re gonna say...
- 18:22...we’re going to, like, set it up.
- 18:26And then otherwise, we’re gonna, like, clean it up.
- 18:28But at the end of the day, for both of these branches,
- 18:31we’re gonna set...
- 18:37So this will toggle the visibility of our frame.
- 18:41So if it’s not visible,
- 18:42we want to do the setup stuff for the skill tree.
- 18:45So I’m gonna make a local function. Say...
- 18:50It’s gonna have no parameters, and in here,
- 18:52first of all, we’re going to
- 18:54retrieve the skill info from the server by saying...
- 19:01That’s all we need to do, and this will retrieve
- 19:03our local player’s skill-tree info, which is great.
- 19:05And then we’re gonna run through name, info in playerSkillInfo,
- 19:11just like we did on the server.
- 19:12But here, what we’re gonna do is we’re gonna get the selector.
- 19:16So notice how in the folder of NodeSelectors,
- 19:20I named each of these exactly the same
- 19:22as I did on the server with the, like, names up here,
- 19:26and that’s purposeful, because they cover the thing
- 19:29that you’re dealing with on the client.
- 19:32Like, just the local image stuff.
- 19:35So we’re gonna say nodeSelectors...
- 19:40Um, we're gonna say local...
- 19:48So we’re gonna find the thing,
- 19:50and then we’re gonna set selector.Visible equal to true.
- 19:53Because if it’s in the playerSkillInfo,
- 19:56it’s not unlocked, so we need to cover it up
- 19:59with that selector button.
- 20:00And also, we need to do selector.Activated,
- 20:05connect a function where we’re going to –
- 20:08once our selector’s clicked,
- 20:10we’re gonna set the currentSelection
- 20:12equal to the name. So the currentSelection’s a string.
- 20:15It’s just gonna be the name of the currently selected skill,
- 20:18and we’re also gonna reflect that on the unlock button.
- 20:20We’re gonna say...
- 20:25I’m just using string.format() to make concatenation easier.
- 20:28We’re gonna say...
- 20:32Then I’m gonna do %ipts [points].
- 20:36So – and then we’re gonna pass in
- 20:39name and then info.Points.
- 20:41So what we’re doing here is, string.format() just takes in –
- 20:45this little percent sign means, like, a parameter
- 20:47that’s going to replace this, so the name is going to replace
- 20:50our – this thing right here,
- 20:51and the i means, like, it’s an integer,
- 20:53so, like, for numbers and stuff.
- 20:54So this little info.Points is going to replace
- 20:57this %i kind of thing here.
- 21:01And then we’re doing info.Points.
- 21:04That’s going to be this value right here.
- 21:06So it’s gonna be, like, you know, all this stuff here.
- 21:08And... yeah.
- 21:10So this should work out just fine.
- 21:12But look at how we’re creating a new hook,
- 21:17event connection, every single time
- 21:19we open up our panel.
- 21:22Well, we are when we run this.
- 21:24So when we run this function in every single activation,
- 21:27we’re creating a new connection.
- 21:30That’s a memory leak. We don’t want that.
- 21:31So we’re going to have to make a list of connections.
- 21:36So we’re gonna say selectorConnections
- 21:38equals a blank table,
- 21:41and then – oh, I spelled selector down here wrong.
- 21:43That’s interesting.
- 21:44There we go. We fixed that.
- 21:45But before this activated, we want to say...
- 21:54So it’ll add our connection to this list,
- 21:59and then once we’re done – so if we don’t...
- 22:03if it is visible and we’re closing it –
- 22:05we want to get rid of all of those connections.
- 22:07So we’re gonna run through all of them. We’re gonna say...
- 22:18And then we’re gonna call table.clear()
- 22:21on the selectorConnections.
- 22:23So, what we’re doing here
- 22:26is basically just disconnecting all of our connections
- 22:28because we don’t want a memory leak.
- 22:30We don’t want these running and being able to click
- 22:32in the background, and it’ll just –
- 22:34it’ll cause a whole bunch of issues. We don’t want that,
- 22:36so we clean this up when we close it.
- 22:38And, realistically, we don’t have to do this
- 22:40at every single opening and closing of the GUI,
- 22:44but just for, like, you know, in case something goes wrong
- 22:47and you wanted to just start fresh from the server,
- 22:49you can just do this, which I think is a good strategy
- 22:52just to make every time you open and close it,
- 22:55it’ll be good to go.
- 22:57So now we should be able to try it out.
- 23:00Let’s see how it goes.
- 23:02Okay, so we click this... Oh.
- 23:06Dang it. Um, I spelled Activated wrong.
- 23:08That is tough.
- 23:10Let’s go back and fix it.
- 23:12There we go.
- 23:14Remember to spell correctly, kids.
- 23:17Now we click this.
- 23:18We get it, and we click on different things
- 23:21so we can see it has all the stuff.
- 23:23So when we click on each button,
- 23:26or each little shroud on top of our skill,
- 23:29it changes the text to the unlock thing,
- 23:33and it all works out in the end.
- 23:35Cool.
- 23:36Now the next step is to actually
- 23:40make the unlock button do something.
- 23:42So, below our toggle, we’re gonna say...
- 23:50And then, first of all, we’re going to say...
- 23:55Because if we have nothing selected,
- 23:56we obviously can’t really unlock it.
- 23:58So how are we going to do the unlock stuff?
- 24:01So for that, we’re going to make a new...
- 24:04um, a new –
- 24:06why am I trying to rename ReplicatedStorage? –
- 24:08a new remote function,
- 24:10and it's going to be named TryUnlockSkill.
- 24:14So, this is a remote function
- 24:16because we’re gonna return a boolean, like, success,
- 24:19which is gonna say whether or not
- 24:21the unlock was actually successful.
- 24:24So the client’s gonna go petition the server
- 24:26and say, “Hey, let’s try to unlock this,”
- 24:28and the server’s gonna be like, “Okay, we’ll see,”
- 24:30and it’ll say, “Okay, return true;
- 24:31it was good and successful,”
- 24:33“Return false” if it’s not successful.
- 24:35So... we’re gonna first get it.
- 24:38Up top, we’re gonna say local tryUnlockSkill
- 24:43is going to be equal to...
- 24:49Cool.
- 24:50And then we’re going to say...
- 25:00...and we’re gonna send it the currentSelection.
- 25:02So we’re gonna say, “Hey, we’re gonna try to unlock
- 25:04this currentSelection,” this string here,
- 25:06and then it’s going to give us a success boolean.
- 25:09I’m gonna say “If it’s successful, then...”
- 25:11We’re gonna say...
- 25:18And then we’re gonna say...
- 25:26So what we’re doing here is, if it’s successful, we take that
- 25:30specific node selector. So let’s just say we unlock Fire.
- 25:34Let’s make this visible.
- 25:35We unlock Fire and we’re like, “Okay, we unlocked Fire.”
- 25:38But we don’t need this shroud over anymore,
- 25:40so we just delete that selector.
- 25:42Or we don’t delete it, we make it invisible,
- 25:44and then we disconnect the...
- 25:49the connection so that we can’t click it
- 25:51and update the button any longer.
- 25:53You could, I guess, delete it, but, I mean...
- 25:57I don’t know. It’s just, like, more destructive.
- 25:59This is simpler. It can lead –
- 26:01if there’s an issue or something down the line,
- 26:03the GUI will still work in the long run,
- 26:05even though I don’t think a skill unlock
- 26:09should ever be revoked in a game, but, you know,
- 26:11this is just a better habit to get into, I’d say.
- 26:15So now, we did this,
- 26:17but now we need to implement this in the server.
- 26:19So let’s go to our server script, and then up at the top,
- 26:22we obviously need to get it from ReplicatedStorage,
- 26:25so tryUnlockSkill will be equal to...
- 26:33There we go.
- 26:34Now we need to say...
- 26:40...will be equal to a function which takes in the player,
- 26:43and now we also have another parameter, the skill name,
- 26:46which is going to be a string. So...
- 26:49first off, we need to double-check
- 26:52if the skill name exists, so...
- 26:57This is just a good practice
- 26:59since we don’t really know what the client will send to this,
- 27:02because exploiters could, like, send some junk to it.
- 27:04We want to make sure, like, if it doesn’t exist,
- 27:06if skill name doesn’t exist, then we just kill it off the bat.
- 27:09Otherwise we’re gonna say...
- 27:19So this is another defensive-coding strategy.
- 27:22We need to make sure the player exists,
- 27:25and then we also need to check if leaderstats exists
- 27:29using FindFirstChild().
- 27:29We don’t know. Like, something could have happened
- 27:31where it got deleted or, like, whatever. We're gonna say...
- 27:46That is not what I want to do.
- 27:53And make sure this string in this FindFirstChild()
- 27:56is the same as this one up here.
- 27:58It’d be good to make a constant, but, you know, whatever.
- 28:01So, why we’re using and here is because this and
- 28:04returns the first argument if it’s false
- 28:08or the second one if it’s true.
- 28:10And true and false in this case means “exists.”
- 28:12So if the player exists – or if the player doesn’t exist,
- 28:14we return nothing, and then if leaderstats doesn’t exist,
- 28:17we return, like, false, or nil.
- 28:19And then, if it does exist, we return this
- 28:21and we go here and then we go here.
- 28:23So it’s just, like, basically little tiny if statements,
- 28:26if-else statements, with nil and stuff,
- 28:28but it’s just simpler and pleasing to the eye
- 28:30to put it this way.
- 28:31And we’re also gonna do the same thing for cost.
- 28:33Like, how many skill points will this cost?
- 28:35And this is gonna be equal to...
- 28:48So, theoretically – actually, with this line,
- 28:50I think this actually might be a little better.
- 28:52Since we’re checking for skillTreeInfo[skillName],
- 28:54if skillName is nil, this actually won’t give us anything,
- 28:58and it’ll short-circuit.
- 29:00So...
- 29:03Actually, no. It’s good now. Um...
- 29:07We can just delete this guard clause up here
- 29:10because we already handle it with this kind of, like, check here,
- 29:13and you’ll see why this is handled in a second,
- 29:15when we make the if statement.
- 29:17But this is just the cost, the number of skill points
- 29:18that our skill costs. So we’re gonna say...
- 29:28So this cost also means that, like,
- 29:31skillTreeInfo.Name is –
- 29:33or skillTreeInfo[skillName] actually exists.
- 29:36So this assumes this,
- 29:37and you could obviously, like, replace that too, but –
- 29:40so we have the skill points and the cost.
- 29:43Then...
- 29:46we can, um, like, do something.
- 29:48But we need to do some other checks before that.
- 29:50So we need to make sure the –
- 29:52and I’m making a space here just because you’ll see.
- 29:54We’re gonna say and...
- 29:58...is greater than or equal to cost.
- 30:01And this will make sure that our skill points –
- 30:04our current skill points, like, the skill points of the player –
- 30:06is greater than or equal to cost.
- 30:07And we also need to make sure that the skill
- 30:09that they’re trying to unlock has their prerequisite satisfied.
- 30:12So we need to do and satisfyPrereqs(),
- 30:17which will just pass in the player and the skillTreeInfo...
- 30:26There we go.
- 30:27So we need to make this function, satisfyPrereqs(),
- 30:29and it’s gonna be a local function.
- 30:33And all I’m gonna be basically doing here is...
- 30:39...player, prereqs – is going through
- 30:44the skills of the –
- 30:46or going through the prereqs and then making sure it’s in the playerSkills.
- 30:48So we’re gonna get our playerSkills for this.
- 30:50local skills is gonna be equal to...
- 30:59And then we’re gonna say...
- 31:04We just used an underscore here, by the way,
- 31:06just because we don’t care about the first value.
- 31:08That’s just the iteration number. Say...
- 31:16And then, if we get through it without returning false,
- 31:18we return true.
- 31:19So if we don’t find the actual skill in the prereq
- 31:23in our playerSkills – so if the player
- 31:25does not have this prereq, then we instantly abort.
- 31:28We say, “Hey, you can’t get this. Return false.”
- 31:31And then otherwise, if all these get through
- 31:33without triggering this return, we just return true by default.
- 31:37So now we say, “If skillPoints and cost
- 31:41“and it satisfies the cost thing and we satisfy our prereqs,
- 31:46“we satisfied all these things – all these things exist.
- 31:48Everything is good.”
- 31:49And by the way, this, skillTreeInfo[skillName],
- 31:52is guaranteed – the prereqs are guaranteed to exist
- 31:55because cost exists.
- 31:56You can make it more explicit by replacing
- 31:58this little block here down here, but it’s more characters, I know.
- 32:02Do what you want.
- 32:04But in this if statement, we can say...
- 32:14Actually, we could add this to our big if statement.
- 32:18We can say...
- 32:34So this accounts for the case where the player owns the skill.
- 32:37If they own the skill, we don’t want to charge them twice.
- 32:39So if the player doesn’t already own the skill,
- 32:42then we go down here, and we say...
- 32:49So we deduct cost from the current player’s skill points.
- 32:52We add that. So we do...
- 33:02So we add the skill name
- 33:03to our list of skills that the player has,
- 33:06and then we return true because it was successful,
- 33:11and if all this fails, we return false.
- 33:15So, um, sorry for the confusion up here.
- 33:17I just realized I could set the line of code further.
- 33:20So we have to satisfy all of these requirements
- 33:22to get to the point where we can actually deduct our points
- 33:25and then, like, do the insertion.
- 33:27So this adds the skill
- 33:29to this little table that we created earlier up here.
- 33:32So, now that we have all this, let’s...
- 33:35All that’s left to do is try it out, I guess.
- 33:37Let’s do it.
- 33:41Okay, “Skill Tree.” Open. Boom.
- 33:43Fire, costs three points. Unlock this.
- 33:46Boom – three points is deducted. That’s great.
- 33:48Okay, now let’s say that we want to unlock Smoke.
- 33:51We can’t do it. It won’t let us.
- 33:53It keeps on returning false because, you know,
- 33:55we don’t have the satisfied prerequisites.
- 33:57And here we can try to unlock Water. That works.
- 34:00We can unlock Steam. There we go.
- 34:01Unlock Smoke. Boom.
- 34:03And then if we open and close it again,
- 34:05you see there is no shroud over it again.
- 34:08We can unlock Ice. There we go.
- 34:11That is absolutely wonderful.
- 34:13And obviously, we still have this “Unlock Ice.”
- 34:15Like, you can polish this up to as much as you wish,
- 34:18but I hope I provided you
- 34:20the building blocks for your own skill-tree system
- 34:23in your game.
- 34:24And I know I was talking –
- 34:26I felt I was talking pretty fast in this video,
- 34:28but there’s a lot of content to get through,
- 34:29and this is still, like, 30 minutes long,
- 34:31which is kind of crazy.
- 34:32But there could be more offshoot videos off this,
- 34:35so if you want to see something else,
- 34:36like maybe making a procedurally generated
- 34:38skill-tree visualization
- 34:40instead of using the image that I used today
- 34:43or something else along the likes of that,
- 34:46then just let me know.
- 34:49But comment any questions.
- 34:50The code will be in the description.
- 34:53And make sure to like and subscribe if you enjoy.
- 34:56And with that, I hope you have a nice day, and goodbye.
- 34:59(electronic music playing) Captions by rc8s
About this transcript
This page contains the full transcript of How to make a Skill Tree in Roblox Studio (+Graph Theory) by B Ricey, generated from the public captions YouTube serves with the video. The transcript has 5,505 words across 703 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to choose a caption track available on the video.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, caption language selection, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.