HomeVideos

Godot 4.5 Beginner Tutorial: XP Systems, Level Up UI & Pausing (Part 4)

Now Playing

Godot 4.5 Beginner Tutorial: XP Systems, Level Up UI & Pausing (Part 4)

Transcript

601 segments

0:03

Welcome back to the checkpoint. Right

0:05

now, our game is endless but pointless.

0:08

In this video, we're going to fix that.

0:10

We'll create XP gems that drop from

0:12

enemies, a UI to track our progress, and

0:14

a system that pauses the chaos so we can

0:16

choose an upgrade. Killing monsters is

0:18

fun, but getting stronger is addictive.

0:21

Today, we're going to close that

0:22

gameplay loop by adding experience,

0:24

levels, and the most important screen in

0:26

the game, the upgrade menu.

0:29

Well, first we need the loot. This is a

0:32

simple object that sits in the world

0:33

waiting to be picked up. Now we do that

0:35

the scene, new scene. Now we're going to

0:38

use the other node again. We're going to

0:40

use an area 2D

0:42

2D. Enter. Now we're going to rename

0:45

this to experience gem.

0:49

There we go. And then we need to add

0:51

what it looks like. Add child node. Just

0:54

a sprite 2D.

0:56

And bring my file system back up here.

0:59

go to our sprites folder. And once

1:01

again, I'm using some Kenny assets here.

1:04

And what I'm going to use is the little

1:05

coin from the pixel platformer one here.

1:08

I'll link all these in the description.

1:10

And once you get that download, if you

1:12

haven't already, the sprites folder

1:14

selected. Now, the one I'm going to use

1:16

is tile0151.

1:20

If you're following along with that,

1:22

that's going to be the little coin

1:23

thing. All right. And now with sprite 2D

1:25

selected, I can click and drag and drop

1:27

that into the texture here. I zoom in. I

1:31

got a little coin. And then we just need

1:34

to add a to the experience gem. Select

1:36

that. Add child node collision 2D.

1:40

Collision shape 2D. And once again,

1:42

we're just going to use a shape circle.

1:45

We're going to make that make it a

1:47

little bigger than the actual probably

1:50

twice as big as the pickup itself. It's

1:53

a quality of life thing for the player.

1:54

You don't want to make it hard for these

1:56

to pick up. Great. Now, if you remember,

1:58

we need to go to our experience gem and

2:01

our collision. Now, we set up a layer

2:04

already. We want it's not a player, it

2:06

is loot. So, we want this to be a loot.

2:10

And we want the player on the mask to be

2:13

able to detect when it runs into it.

2:15

Great. Now that we have that, we need to

2:17

have a script to tell what happens when

2:19

this collides with the player. the

2:21

experience gem selected. We're going to

2:23

add a new script. Not in the root

2:25

folder. We'll go the path. We're gonna

2:27

make sure we're in the scripts folder.

2:29

Open. Now we're in the scripts folder.

2:31

Create. Let's delete the boiler plate.

2:36

The first thing we want to do is on the

2:37

experience gem selected on the node. If

2:40

this is blank, just click away and click

2:42

back on. We want to get body entered

2:45

again. Just like you did with the

2:46

projectile. Double click. Make sure it's

2:48

on the experience gem. connect. That

2:51

should populate into your script we just

2:53

wrote. I'll make this bigger. Get rid of

2:56

this output. First thing we want to do

2:59

is we want an export variable. So this

3:01

export variable is going to tell us well

3:04

the XP amount that this is going to give

3:07

us. So for now we'll just set it to 10.

3:09

They're automatic entered and get rid of

3:11

this placeholder.

3:13

Now, first thing I do is say, well, if

3:15

the player touches this,

3:18

give them XP.

3:21

There we go. So, we need to check a

3:23

certain method just like we did with the

3:26

enemy, the take damage one. But in this

3:28

one, we got to make sure the body since

3:30

we know the body is the player. Well, if

3:33

it has the method, we're going to write

3:35

this method here in a second just like

3:37

we did the shoot method or the take

3:39

damage one.

3:40

We will with the take damage one. So has

3:42

method gain XP.

3:45

Okay. And after that we're going to go

3:48

into the if statement there. Well, first

3:50

we're just going to print to the console

3:53

experience gained

3:56

colon concatenate plus. We want to turn

3:58

our XP amount into a string. XP amount.

4:04

That'll print to a console. If you don't

4:06

turn that into a string, it will throw

4:07

an error. So make sure you do the str in

4:10

the parenthesis. All right. And then

4:13

after that, well, we're going to call

4:14

the the gain XP method that we're going

4:17

to write here in a second. XP amount.

4:21

Perfect. And then after we've collected

4:24

the gem, well, we want to get rid of it.

4:27

You never want to keep things floating

4:29

around for no reason. So remove the gem

4:33

after we've given the XP. Great. Now

4:36

let's control S to save that. as

4:38

experience gem scene in the scenes

4:41

folder. Save. Now we go back to our

4:44

enemy. Remember that take damage

4:46

function we pretended to have in the

4:48

last video? Well, it's time to write it

4:49

for real.

4:51

Great. Now, let's go to our enemy GD

4:53

script here. Now, we're going to go up

4:55

to the top here. We're going to add

4:57

another export variable here. What this

5:00

is going to do is going to hold the uh

5:02

loot that is going to drop. So, it's

5:04

going to be the loot scene. That's going

5:06

to be another pack scene here.

5:09

And then we'll scroll down to the

5:10

bottom. What we're going to do here is

5:12

add that take damage function function

5:15

take damage.

5:17

Great. The first thing we want to do in

5:19

this take damage. Well, we want to spawn

5:21

the gem because if we're going to die in

5:24

one hit. Now, once your enemies have

5:26

health, well, you want to spawn the gem

5:27

just when they die, not every time they

5:29

get hit. That's something we can add in

5:31

the polish section later. So, first

5:33

thing is var new gem. I'm going to store

5:36

this new gem in in a variable. And we're

5:38

going to do that by instantiating the

5:40

loot scene we established up top and

5:42

instantiating it. Perfect.

5:45

And the with this new gem, we're going

5:48

to need the global position of well the

5:52

enemy, which is what this is script is

5:56

attached to. So, we can just write

5:57

global position. Now, this enemy is

5:59

going to get deleted here in a second.

6:01

So, we don't want to add it to the enemy

6:05

tree or the enemy uh as an enemy to a

6:08

child to the enemy. We want to once

6:10

again add it to a child of the main

6:13

scene. So, if you remember, it's going

6:15

to be get tree

6:18

current scene. Now, we're going to do a

6:20

little differently because you can't

6:22

modify the physics tree by adding and

6:24

removing bodies during a collision call

6:27

back without risking crashing. So we're

6:30

going to use a thing called call

6:31

deferred. Now this waits until the end

6:34

of the frame so it does it safely. So

6:38

call deferred.

6:40

Now you use this a lot going through. It

6:43

saves a lot of headaches. So call

6:46

deferred is going to save us from doing

6:48

too many things at once. So what is call

6:51

deferred going to do? Well, we need it

6:53

to add child

6:56

there. We're going to call the add child

6:58

function in call deferred. So when the

7:00

frame ends and when what child do we

7:04

want to add? Well, we just want to call

7:05

the new we're going to use the new gem.

7:08

So call deferred is going to wait till

7:10

the end of the frame. So anything that's

7:12

happening in the physics can finish and

7:14

then it's going to add this to the

7:16

current scene root node just like we

7:19

need to. And well last thing we need to

7:22

do Oh die. Yep. So we're going to kill

7:24

the enemy. It's going to be cube free.

7:26

Now everything's done. This gem is

7:28

spawned. Now the enemy can go away. Now

7:31

none of this will work if you don't

7:32

assign the projectile. So I'm going to

7:35

minimize this here quick. Experience.

7:38

I'm go to the main or the enemy scene

7:40

here. Go back to my inspector node and

7:43

my enemy here. Scroll up to the top. I

7:46

have a loot scene here. So the loot

7:48

scene is the scene I want to spawn when

7:51

I die, which is the experience gem here.

7:54

Drag and drop that there. Now the enemy

7:57

knows what loot to drop when it dies.

8:00

Now control S to save. Now the gem tries

8:03

to call gain XP on the player, but

8:06

remember that doesn't exist yet. So

8:08

let's add the stats to our hero. The

8:11

first thing we need to do is open up the

8:12

player GD script. I'll make this bigger.

8:16

And we need to add some variables up at

8:18

the top here. So we'll do another export

8:21

variable. We can edit this as your heart

8:24

desires, but we're going to start it

8:26

with bar health equals 100. That's good

8:30

for now. We need some normal variables

8:33

that the script just needs access to,

8:35

which is going to be the experience. We

8:37

start with zero. We also need the level

8:40

of the player. So, experience level

8:42

start at one. And then the requirement

8:45

for each level, which we can edit as the

8:48

levels get higher. So the first level is

8:51

required is going to be 100. We also

8:53

need to add I'm add up top here. Enter

8:56

down a couple times and add some

8:58

signals. So signals is going to let

9:00

signals that let the UI know when things

9:03

change. Perfect. Now what this ones we

9:07

need, we need a signal for I spell it

9:10

right. Signal for the experience gained.

9:14

So, we update the progress bar and the

9:16

experience gained.

9:19

And we we need the growth data, which

9:22

we're going to use to make the bar go up

9:24

as we get more experience. And we also

9:26

need to signal when we've leveled up.

9:30

So, when we've leveled up is going to

9:32

show us, well, when the level up screen

9:34

is going to pop up.

9:36

All right, I'll scroll down to the

9:37

bottom here. Enter down a couple times

9:39

after our process function. enter back

9:42

and write a new function called gain XP.

9:46

And how much XP? Well, the amount is

9:48

going to be passed in. Great. So now we

9:52

need to well, we need to take the

9:54

experience and add the amount to it.

9:58

Just like that. Easy enough. And then

10:01

what we need to do is emit the signal so

10:05

the UI can update our experience bar. So

10:08

the leveled up leveled

10:11

I spelled right. Leveled up. There it

10:12

is. Leveled up. That emits the signal

10:16

that we just wrote up here

10:19

that we have leveled up. Perfect. And

10:23

then after we'll enter down a couple

10:24

more times, we write another function

10:26

called level up. Level up. And what

10:31

happens when we level up? Well, we're

10:32

going to update the experience level by

10:35

one. Just like that. Plus equals one.

10:38

And we also need to update how much more

10:41

experience it's going to require for the

10:42

next level. Then we want to be 100 every

10:44

time. We want to make it harder as we

10:47

go. So we're going to plus equal by 50

10:50

every time. So this just makes uh the

10:54

next level harder. The next level

10:56

harder. Perfect. And then here, well,

10:59

after we've done that, well, we're going

11:00

to pause. So we'll pause the game here.

11:06

And then oh we'll do that in part five

11:09

in the next video or next uh section.

11:13

Same video. Perfect. Part five.

11:17

So for now what we're going to do is

11:19

just print. Let me finish up this

11:21

function. See if it works. Level up.

11:24

And it's going to be new level

11:28

is going to be with a comma. Experience

11:30

level. This will just add the experience

11:33

level to the end of the print statement.

11:36

So, new level will be the current

11:38

experience level we're on. Great. Now,

11:40

before we need to tell the gain XP here,

11:44

we'll go in the middle after the

11:46

experience amount. Well, we need to

11:48

check if we've leveled up

11:51

after we've added the amount to the

11:53

experience. So, we're going to check for

11:56

level up. Then, we can call that

11:58

function that we just wrote. So if the

12:00

experience

12:02

is greater than or equal to the

12:05

experience gained,

12:08

no not gained, required. Experience

12:10

required. That's the one we're looking

12:12

for. And what we need to do there is

12:14

just take the experience minus equal the

12:18

experience required. We're going to

12:20

reset that

12:22

there so you can start leveling up on

12:25

the next one after the level up function

12:28

has been fired. We're going to check if

12:31

our experience is enough. We're going to

12:33

reset our experience and then we're

12:34

going to level up. All right, with all

12:36

that done, we can save. Go to our main

12:39

game here and make this smaller and then

12:41

run the current scene and give this a

12:44

test and go around shoot the enemies.

12:47

collect the coins and we pull this up a

12:50

little bit. We can see every coin we

12:52

collect here is 10. So if we get enough

12:55

coins, we need 10 coins to level up. So

12:59

if we get to coins, 10 10. And now we're

13:02

level two. Perfect. That is working as

13:05

intended.

13:08

Now you've reached a checkpoint. If you

13:10

run the game now, you can shoot the

13:11

enemies. They drop the gems. You collect

13:13

them. Check the output tab and you can

13:15

see all the print messages. But the

13:17

player without that window has no idea

13:20

they're progressing. We need a heads up

13:22

display, a HUD. Now, if you never

13:24

created one before in GDAU, it's a node

13:26

called a canvas layer. Now, this special

13:29

node ensures our UI is going to stay

13:31

stuck to the screen even when the camera

13:33

moves around. So, let's build that UI.

13:36

We'll keep it simple. A progress bar at

13:37

the top of the screen. So, I go to our

13:40

main game scene, 2D tab here. Make this

13:44

a little smaller here. Get rid of that.

13:46

Make this bigger. Perfect.

13:49

Now, for this, we're just going to

13:50

attach it to the main game. You can

13:51

attach to the player, but for

13:53

simplicity, we're just use the main

13:55

game. Right click, add child node. We're

13:58

going to use the canvas layer. Perfect.

14:01

So, create that. And then, as a child of

14:05

this canvas layer, we're going to add a

14:06

progress bar right here. Create. And

14:11

then in the game window here, we're

14:13

going to use these anchors. We're going

14:15

to do top wide. Just like that. We go

14:20

into here and scroll in and see our

14:23

progress bar right there. Now that we

14:25

have that, we can go to our canvas

14:27

layer. I'm going to rename this to UI.

14:31

And then we're going to attach a script

14:32

to this UI. Right click, attach script.

14:36

And now in our scenes folder, go to our

14:38

path up our root node and scripts

14:41

folder. UI.gd open. Defaults are fine.

14:46

I'll make this bigger. I'm going to get

14:49

rid of the

14:52

boilerplate code just by habit. So what

14:56

we want to do now is we want to have an

14:58

export on top to get all the player so

15:02

we know where and what to track. So

15:04

we're going to export the player and

15:06

that's a character body 2D.

15:09

And then we also need in a on ready

15:12

check. So when this this node this

15:15

script is attached to is ready, we want

15:17

to get the XP bar which is going to be

15:21

the progress bar. So once again, money

15:24

symbol progress. That should pop up

15:25

automatically unless you did not or you

15:28

did rename it something else. You just

15:30

want to make it so it matches your path.

15:33

Now we have both of those. We actually

15:35

need to make that ready function

15:38

function ready. First thing we want to

15:40

do is initialize

15:42

the bar.

15:44

Initialize there we go bar. So when this

15:48

is ready we start well we want to set

15:49

the bar initially to zero. The value to

15:53

zero.

15:55

So and then we set the max value of the

15:58

bar

16:00

to 100.

16:03

Just like that. So, we want to go from

16:04

zero to 100. That's how we're starting

16:06

out. Okay. Now, we have that set up.

16:09

Let's make another function. We're going

16:10

to call this one. Enter down a couple

16:12

times. Function on experience

16:16

gained.

16:18

Okay.

16:19

We're going to take in a couple things

16:21

called current experience and then our

16:24

max XP. And so we're going to check our

16:27

bar value and make that the current

16:31

that's passed in to our current XP. And

16:34

then we need to check the max value if

16:36

that's gone up. If we've leveled up, we

16:39

want to check the XP bar max value and

16:42

set that to the new max XP. Perfect. But

16:46

we want to be able to track the player.

16:47

So we can't get any of this if we don't

16:49

have the player. So we go into our ready

16:51

function again, enter down a couple

16:53

times. So we want to connect to the

16:55

player but safely. Connect player but

16:59

safely. So we want to have an if

17:01

statement here. If the player exists,

17:03

then we want to connect. How do we

17:06

connect? Well, we have the player. We

17:08

want to connect the experience

17:11

experience gained. So we look at our our

17:16

player script.

17:18

Again, we have the scroll to the top in

17:23

our player script, we have the

17:24

experienced gain signal. That's what

17:26

we're connecting to. Back in our UI

17:28

script, our player experience gain

17:32

signal. We want to connect to that. And

17:35

we're connect to that with our function

17:37

we just wrote on. We want to pass that

17:40

in experience gained. We do not want the

17:43

parenthesis on the end, just the one.

17:46

So, we're going to send in to this

17:48

signal when this experience is gained.

17:51

So, we can update

17:53

appropriately. Now, none of this will

17:56

work if we do not assign the player to

18:00

our export variable. So, I'll make this

18:02

smaller. And in our UI selected here, we

18:05

have in our inspector a player section.

18:09

Now, we want to drag our player that we

18:11

have in our main game and drop into our

18:13

player. Now the UI knows where the

18:16

player is. Now finally, when we hit the

18:18

limit, we want the game to stop. We need

18:21

a level up popup.

18:24

Now on our UI screen here, go back to

18:26

our 2D so we can see what's happening.

18:29

Make this a little smaller.

18:32

Now with this UI selected, right click,

18:34

add child node. We're going to add a

18:36

panel. Now this panel here, I'm going to

18:39

create. We're going to rename this panel

18:43

level up panel. Now, we're going to

18:46

anchor this. And right here in the

18:48

anchor to the center. And then in the

18:52

layout, I'll make this bigger.

18:54

Try it. There we go. And our layout in

18:56

the inspector with the level up panel

18:58

selected. I'll make the custom minimum

19:00

size 400x

19:04

then 500 Y. It's a decent size to take

19:07

up our viewport. And then in this level

19:10

up panel, right click. I'm going to add

19:13

a child node. And I'm going have a label

19:17

right there.

19:19

And then this label I'm going to anchor

19:22

to the top center right there. And I'm

19:26

also going to have horizontal alignment

19:28

with the label selected to center. And

19:31

in the text field going to say level up.

19:36

Great. Now, this level up panel

19:37

selected. Well, I'm going to hide that.

19:40

We don't want to see that until we level

19:41

up. And back in our scripts, let's go

19:44

there. And I'll expand this out. We want

19:46

to go in our player GD script here.

19:51

We need to handle the pausing. So, we

19:52

need to go to our update. We need our

19:54

level up. We need to update our level up

19:56

function. So, our filter methods level

19:58

up. It's on the bottom. Well, after our

20:02

experience required and all that stuff's

20:04

updated, well, we're going to pause the

20:06

game. Pause the game. How do we do that?

20:10

Well, we get the whole scene. We get

20:12

tree and we need to pause and we make

20:16

that true. So, now the game will be

20:19

paused soon as we level up. Now, instead

20:22

of printing, what we're going to do now

20:24

that we're paused, well, we need to show

20:26

the UI. Show the UI. Not shot. show

20:30

there. So, we need a reference a signal

20:34

to actually show the UI. So, the one

20:38

we're going to emit is the emit the

20:40

signal

20:42

leveled up.

20:44

There we go. So, we have it there. So,

20:47

the one we're calling up here. So, if we

20:49

scroll to the top, we have our signal

20:51

leveled up. That's what we're signaling

20:54

that we've leveled up and show the level

20:57

up screen. But if we send the signal,

21:00

it's currently falling on deaf ears

21:01

because nobody is listening. So let's go

21:04

back to our UI GDScript.

21:06

Control S and save all these scripts.

21:09

Scroll down the bottom underneath

21:11

experience gained. We're going to make a

21:12

new script called on level up.

21:18

And then when we have the level up

21:20

signal that we hear and use the money

21:22

symbol, level up panel and make sure you

21:26

this doesn't pop up. Then maybe you

21:28

named it different. This is has to match

21:30

or it won't work. So level up panel

21:33

visible

21:35

there. And we're going to make that

21:38

true.

21:40

In order to actually hear what the

21:42

signal was from the player, we need to

21:44

connect it just like we did with the

21:46

experience gained. So in here, enter

21:49

down. We're going to do player leveled

21:51

up.

21:53

Leveled up. going to connect to that

21:56

signal and we're going to pass in our

21:58

function on level up.

22:02

It's going to call the function that we

22:03

have on level up. Make this visible.

22:07

Now you reached a checkpoint. Now if you

22:10

run this game now and you level up,

22:12

everything pauses. Can't click buttons,

22:15

can't do anything. Now there's a certain

22:18

option you can select to make the level

22:19

up screen immune to the pause. Now, in

22:22

the UI canvas layer, it needs a process

22:25

mode always or when paused. If you leave

22:28

it on the inherit, the UI is going to

22:29

freeze along with the enemies, and you

22:31

won't be able to click anything.

22:34

So, our UI here, our UI panel, we want

22:37

to go to our process. You want to go to

22:40

our mode. You want to make sure when

22:43

paused or always. We can use when paused

22:46

for now. So, when the game is paused, we

22:48

can still use all of our UI stuff.

22:51

Great. Now, with all that connected,

22:53

let's try this current scene. All right,

22:58

run around an enemy. Collect a coin.

23:01

That should not pop up right away. We

23:03

got a bug here. Let me take a look.

23:06

Okay, after looking in our player GD in

23:09

our gain XP function,

23:13

accidentally was calling leveled up

23:15

right away. We don't want to do that. We

23:17

want level up to handle that. So it

23:18

actually pauses. So we want to get rid

23:21

of this signal here. And we want to

23:25

actually make a call something else

23:28

which is the experience gained.

23:31

So we're not leveling up. We just want

23:32

to see the experience gained emit, not

23:35

the leveled up emit. My fault. So we

23:38

want to emit the experience that we have

23:41

and the experience required.

23:44

There we go. We're leveling up a little

23:46

too soon, but now we're emitting the

23:49

experience gained, which will fix that

23:51

bug. Let's go and give it another test

23:54

after we save main game scene. And then

23:58

we'll minimize this or 2D. We're in

24:02

current scene. Now, let's try blasting

24:04

some enemies and collecting some coins.

24:07

All right, perfect. They got one, 10,

24:10

20,

24:12

30, 40,

24:14

50, 60, 70, 80, 90, and level up. There

24:19

we go. Our game pauses and level up

24:22

screen pops up and our progress bar

24:25

resets to zero. Great. Now, if we run

24:28

the game, we can blast the horde,

24:30

collect the gems, watch the bar fill.

24:32

Apart from a bug, well, we fixed it.

24:34

That's part of gamede dev is debugging

24:36

our projects when we call things too

24:38

early or not at all. So, and when it

24:41

hits the max, boom, the game freezes and

24:44

your level up screen appears. So, of

24:46

course, right now we're stuck in the

24:48

pause screen forever. So, in the next

24:50

video, we're going to fix that by adding

24:51

upgrade cards. Now, there going to be

24:53

buttons that let us choose new powers or

24:55

upgrades once we have. Then, we need to

24:57

unpause the game and make our character

24:59

even stronger. Now, if you found this

25:01

helpful at all or fun, leave a comment

25:03

what your GDO game dev goals are. I love

25:06

seeing your stuff. Now, please like,

25:07

subscribe, and hit that notification

25:09

bell. It really helps out the channel.

25:11

And thank you to all our current and

25:12

past Patreon and coffee members. Your

25:14

generous support keeps the channel

25:16

moving. Now, if you want early access to

25:18

tutorials, source code, or suggesting

25:20

future tutorials, please consider

25:22

becoming a member yourself. The links

25:24

are in the description. I'm Spaghetti

25:26

Syntax and remember, finished not

25:28

perfect, fail fast, fail often, always

25:31

stay curious. And I'll see you at the

25:34

next checkpoint.

Interactive Summary

This video focuses on adding core gameplay mechanics to an endless game, specifically experience gems, a UI progress bar, and a level-up system with an upgrade menu. It covers creating the XP gem as a scene with collision, scripting its behavior to grant XP to the player upon collection and then self-destructing. The enemy script is updated to drop these XP gems upon death, using `call_deferred` to safely add the gem to the scene. The player script is enhanced to include variables for health, experience, level, and experience required for the next level. It also introduces signals for 'experience gained' and 'leveled up' to communicate with the UI. A UI canvas layer is implemented to display a progress bar that updates as the player gains experience. Finally, a level-up panel is created, and the game is set to pause upon leveling up, with the UI configured to remain responsive. A bug where the 'leveled up' signal was emitted too early is identified and fixed by ensuring the 'experience gained' signal is used correctly.

Suggested questions

6 ready-made prompts