Learn Godot 4 by Making a 2D Platformer — Part 16: Level Progression #1
Tags: gamedev, programming, beginners, tutorial
Published: 2023-08-03 03:01:00

**You can find the links to the previous parts at the bottom of this tutorial.*

With our enemy obstacles and pickups set up, we need to be able to progress onto the next level when we reach the top of our scene. To do this we can create a simple door that will trigger our level progression menu to show which will allow our player to restart their current level, or progress onto the next one.

---

***WHAT YOU WILL LEARN IN THIS PART:***

* *UI creation.*
* *How to add scene transitioning via PackedScene resources.*
* *How to work with the String object.*
* *How to work with the Time object.*

---

## **STEP 1: SCENE SETUP**

In your project, create a new scene with an Area2D node at its root. Add a CollisionShape2D node, Sprite2D node, and a CanvasLayer node to this scene and save it underneath your Scenes folder as “LevelDoor”. Rename the nodes as indicated in the image below.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*Lr9CdmTdrDh39tv-iTAhSw.png)

Change your Sprite2D node’s texture to “res://Assets/Kings and Pigs/Sprites/11-Door/Idle.png”, and change your CollisionShape to be as follows:

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2032/1*_P76hdayd9e7MdTHLvMyYw.png)

If you instance your door in your Main scene, it should be placed in a position where the player can run into the collision shape without being blocked.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3038/1*x9kIHR7jGRkpnQVUVbfTkw.png)

## **STEP 2: SCENE UI**

The next part is going to be quite a bit of UI work, so buckle up! To your UI canvas-layer node, add a new ColorRect node. Rename it to “Menu”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*hnYlZWnZ7co20dIb9mXOYA.png)

To your Menu ColorRect add a new ColorRect called “TimeCompleted”, which contains two Label nodes as its children. This will show the time in seconds that our player took to complete the level.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*ICkRhxEyMRh5Lf84Gf6Hpw.png)

Duplicate the TimeCompleted ColorRect twice so that we have elements to display our final score and ranking.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*TTWdHSpkkrmKhcYKFljHiw.png)

To your Menu node, add two [Button](https://docs.godotengine.org/en/stable/classes/class_button.html) nodes. These buttons will be used to continue or restart to our defined levels. Call one “RestartButton” and the other one “ContinueButton”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2778/1*tM6s5M2HTdzZ_vmvwVoHdw.png)

Select your Menu node again and add a Label node and a ColorRect node. Rename the ColorRect node to “Container” and move your buttons and your other three ColorRects into this node.

To your Container node, add a Label node to it and move it to the top. Add another ColorRect and change its name to “Border”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*lCqxiiirF2RRBFgXce9PoA.png)

Then, to your Menu node add a Tilemap node with two AnimatedSprite2D nodes. Name one AnimatedSprite2D node to “AnimatedKing” and the other one to “AnimatedKing”. These are just visuals.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*pDQxDuArClhBDyMABGMr6g.png)

**It should look like this so far:**

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*q1GNR-H7jPJ3zDGKvPFFWA.png)

***1.*** ***Menu***

I’m going to take you through from the top down. Select your Menu node and change its color property to #232f2b. Change its anchor preset to full-rect.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*Klnh1mC3Cl8AQRADtFT0tw.png)

***2.*** ***Container***

Then select your Container node and change its color property to #30403a. Change its anchor-preset to full-rect, and its size (x: 1078, y: 590) and position (x: 35, y: 27).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*s_aHW-l8U23KVYbsCKUbOw.png)

***3.*** ***Label***

Select your Label node and text to “Level Complete!”. Change its anchor-preset to center-top, and its size (x: 504, y: 39) and position (x: 269, y: 80). Also change its font to “QuinqueFive” and the size to “30”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*ugaJoh0_cBjyxJd4RLqJeQ.png)

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*6Uo6iubgp_MWo5k58psP2A.png)

***4.*** ***Border***

Select your Border node and change its color property to #455851. Change its anchor-preset to the center, and its size (x: 600, y: 350) and position (x: 239, y: 145).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*pX5ZFN3zphPi0Vrpac33vg.png)

***5.*** ***TimeCompleted***

Select your TimeCompleted node and change its color property to #ffffff00. Change its anchor-preset to h-center-wide, and its size (x: 1078, y: 0) and position (x: 50, y: 200).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*81qMN0zY9305d6OcTMPilg.png)

***6.*** ***TimeCompleted > Label***

Select your Label node inside your TimeCompleted node and change its text to “Time:”. Change its anchor-preset to center-left, and its size (x: 336, y: 27) and position (x: 210, y: 0). Also change its font to “QuinqueFive” and the size to “20”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*E6fk7ywY_NDoRfNYiUQMAQ.png)

***7.*** ***TimeCompleted > Value***

Select your Value node inside your TimeCompleted node and change its text to “0”. Change its anchor-preset to center-right, and its size (x: 400, y: 27) and position (x: 410, y: 0). Also change its font to “QuinqueFive” and the size to “20”. You also want to change its font color to #0000005a (underneath Theme Overrides > Colors > Font Colors).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3838/1*Bp5Aaz1uq3OMNHt_ykhu-Q.png)

***8.*** ***Score***

Select your Score node and change its color property to #ffffff00. Change its anchor-preset to h-center-wide, and its size (x: 1078, y: 0) and position (x: 50, y: 220).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*QsXtmjtS9XJET8PY30qH4A.png)

***9.*** ***Score > Label***

Select your Label node inside your Score node and change its text to “Score:”. Change its anchor-preset to center-left, and its size (x: 144, y: 27) and position (x: 210, y: 40). Also change its font to “QuinqueFive” and the size to “20”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3838/1*xHtuCazMlaHh-v7cl77qwQ.png)

***10.*** ***Score > Value***

Select your Value node inside your Score node and change its text to “0”. Change its anchor-preset to center-right, and its size (x: 400, y: 27) and position (x: 410, y: 40). Also change its font to “QuinqueFive” and the size to “20”. You also want to change its font color to #0000005a (underneath Theme Overrides > Colors > Font Colors).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*liKN_2Istn_syoavGfETbg.png)

***11.*** ***Ranking***

Select your Ranking node and change its color property to #ffffff00. Change its anchor-preset to h-center-wide, and its size (x: 1078, y: 0) and position (x: 50, y: 240).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*2kPz2sJavwctT0ENs6-hSg.png)

***12.*** ***Ranking > Label***

Select your Label node inside your Ranking node and change its text to “Ranking:”. Change its anchor-preset to center-left, and its size (x: 144, y: 27) and position (x: 210, y: 80). Also change its font to “QuinqueFive” and the size to “20”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*OGfKnPJb9TKaOk2rjasdig.png)

***13.*** ***Ranking > Value***

Select your Value node inside your Ranking node and change its text to “0”. Change its anchor-preset to center-right, and its size (x: 400, y: 27) and position (x: 410, y: 80). Also change its font to “QuinqueFive” and the size to “20”. You also want to change its font color to #0000005a (underneath Theme Overrides > Colors > Font Colors).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*bkM32CYAbKTf6QJGxID2Mw.png)

***14.*** ***ContinueButton***

Select your ContinueButton node and change its text to “Continue”. Change its anchor preset to bottom-left, and its size (x: 260, y: 90) and position (x: 265, y: 385). Also, change its font to “QuinqueFive” and the size to “25”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*BzbPBc8E3X-HbhAesDVIXw.png)

***15.*** ***RestartButton***

Select your RestartButton node and change its text to “Restart”. Change its anchor preset to bottom-left, and its size (x: 270, y: 90) and position (x: 545, y: 385). Also, change its font to “QuinqueFive” and the size to “25”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*amEt65oMpI4oEMedQAYbig.png)

***16.*** ***TileMap***

Now, for your TileMap node, assign the “Terrain.png” tilesheet to your TileSet resource and draw in the shapes of a tower.

**It could look something like this:**

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2032/1*0JBAc4vLjbHUvwDnrO5JDw.png)

***17.*** ***AnimatedPig***

Create a new animation for it with the “res://Assets/Kings and Pigs/Sprites/04-Pig Throwing a Box/Idle (26x30).png” resource. Add all the frames from the spritesheet. Change the FPS value to 8 and leave the looping on. Click the icon next to the trash can to enable this animation when the game starts. This will play this animation on the pig when the menu is shown.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2076/1*owRmx1Uog7CvYvD7xicQZA.png)

***18.*** ***AnimatedKing***

Create a new animation for it with the “res://Assets/Kings and Pigs/Sprites/01-King Human/Idle (78x58)2.png” resource. Add all the frames from the spritesheet. Change the FPS value to 11 and leave the looping on. Click the icon next to the trash can to enable this animation when the game starts. This will play this animation on the pig when the menu is shown.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2070/1*ZUjCcA697xlZGRK0xx-5BA.png)

**The complete UI layer should now look similar to this:**

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2008/1*M_QozMYUwP0ykWQs_gT71Q.png)

We want this UI Menu to slowly pop up on the screen when the player enters the door. To do this we need to add a new AnimationPlayer node to our scene.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*pyqXkobT8l9Lzdkj0oYamw.png)

To this AnimationPlayer node add a new animation called “ui_visibility”.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*flH4sustrzIVXVqkblJ1ZA.png)

Add a new “Track Property” and assign it to your Menu node.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*q1GNR-H7jPJ3zDGKvPFFWA.png)

We want this Menu node to go from invisible to visible, and therefore we need to change its [modulate ](https://docs.godotengine.org/en/stable/classes/class_canvasmodulate.htmlhttps:/docs.godotengine.org/en/stable/classes/class_canvasitem.html#class-canvasitem-property-modulate)value. The modulate value refers to the color applied to textures on this CanvasItem. We will change the modulate value’s alpha value, which is the node’s transparency.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2302/1*eaPiLkEP1EKJ0hMIAtB50Q.png)

Add two keyframes to this animation — one at point 0 and one at point 0.5.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*W5YWGAfBB0RYpfesqrAXhg.png)

Change the keyframe at point 0’s Color value to “ffffff00”. This will make it transparent.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*cdNdQ33_rpjrCOC3viJn-Q.png)

Change the keyframe at point 0.5’s Color value to “ffffff”. This will make it visible.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*tGxfLToPhZ5hj943MCDHoQ.png)

We also want to change the [update mode](https://docs.godotengine.org/en/stable/tutorials/animation/introduction.html) from discrete to continuous. Our update mode tells our animation track how often it should update our animation property. Continuous mode updates the property on each frame. This will make our node slowly go from invisible to visible.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*QBdvfp_3zjzlBjaG8MUrBA.png)

Finally, change your animation’s length to 0.5.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*W5YWGAfBB0RYpfesqrAXhg.png)

Now if you play your animation, your menu should be made visible.

## **STEP 3: SCENE CHANGING**

With our UI set up, we can go ahead and attach a new script to our LevelDoor scene. Save it under your Scripts folder.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*0-tDjBOiKoPWHfenohNkzw.png)

We also want to assign our root node’s (Area2D) *body_entered()* signal to our script. This will trigger our menu to pop up when our player enters the collision body.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2206/1*DGgHVM2J-dOSPJsd_MsP8A.png)

Finally, attach your Button node’s (both ButtonContinue and ButtonRestart) *[pressed()](https://docs.godotengine.org/en/stable/classes/class_button.html#class-button-theme-style-pressed)* signals to your script. This tells our game what to do when the button is pressed.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2220/1*zd6L5pCRPCMIfR8wyZ2-BA.png)

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2212/1*VQ_JRPJ2q61hfEXlcb0Oxg.png)

Before we continue, we need to change our scene’s process mode. Each node in Godot has a “[Process Mode](https://docs.godotengine.org/en/stable/tutorials/scripting/pausing_games.html)” that defines when it processes. This means the node, or the scene will only “work” when a certain processing mode is assigned to it. The modes can be found and changed under a node’s [Node](https://docs.godotengine.org/en/stable/classes/class_node.html#class-node) properties in the inspector.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*s4dvR2K-baFEg0tn0CAigA.png)

**This is what each mode tells a node to do:**

* **Inherit**: The node will work or be processed depending on the state of the parent. If the parent’s process mode is pausable, it will be pausable, etc.

* **Pausable**: The node will work or be processed only when the game is not paused.

* **When Paused**: The node will work or be processed only when the game is paused.

* **Always**: The node will work or be processed if the game is both paused or not.

* **Disabled**: The node will not work, nor will it be processed.

We need our LevelDoor scene to work even if the game is paused. By default, the process mode is set to “Inherit”. This means it will process the same as its parent node. We need to change this to Always since we need this node to work when our game is running — because it needs to capture our player entering its collision — and we also need to have it working when the game is paused — which is when our Menu will be made visible. Let’s change our LevelDoor’s root node’s Process Mode to **Always**. You can find this option under Node > Process > Mode.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*gVensspnca9eym_oeF4bSg.png)

Now our LevelDoor scene will process and execute its functions both when the game is running and when the game is paused. In our script, we will pause our game when the Player’s body enters our doors collision body. When we press the restart or continue button, we will unpause the game so that we can continue playing the game in the next level. If we want to pause the game, we simply use the [SceneTree.paused](https://docs.godotengine.org/en/stable/classes/class_scenetree.html#class-scenetree-property-paused) method. If the game is paused, no input from the player will be accepted, because everything is paused. That is unless we change our node’s process mode to “Pausable” or “Always”, which we already did!
```python
    ### LevelDoor.gd
    
    extends Area2D
    
    func _on_body_entered(body):
        if body.name == "Player":
            # pause game
            get_tree().paused = true
    
    func _on_continue_button_pressed():
        #unpause scene
        get_tree().paused = false
    
    func _on_restart_button_pressed():
        #unpause scene
        get_tree().paused = false
```
We also don’t want our Menu node to be visible by default, or when the player presses our buttons. In other words, when the game is not in a paused state, we want the Menu node to be hidden. If the game is paused, we want to show the Menu node. We will make this node visible by changing its [visibility ](https://docs.godotengine.org/en/stable/classes/class_canvasitem.html#class-canvasitem-property-visible)value in the code, and when our player enters the door, we will play our *ui_visibility* animation to make the Menu appear.
```python
    ### LevelDoor.gd
    
    extends Area2D
    
    func _ready():
        #hide menu on load
        $UI/Menu.visible = false
        
    func _on_body_entered(body):
        if body.name == "Player":
            # pause game
            get_tree().paused = true
            # show menu
            $UI/Menu.visible = true
            # animation to make menu's modular value visible
            $AnimationPlayer.play("ui_visibility")      
        
    func _on_continue_button_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $UI/Menu.visible = false
        
    func _on_restart_button_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $UI/Menu.visible = false
```
When we press our ButtonContinue, we want our player to be moved over to the next level. The next level will depend on the current level that we’re in. In our Main level, the next level will be Main_2, and its next level will be Main_3, and so forth. We don’t want to create a giant conditional statement to assign the next_level based on the current level, since that will be tedious and unnecessary. What we can do instead is export a variable that will hold a [PackedScene ](https://docs.godotengine.org/en/stable/classes/class_packedscene.html)resource. This creates a resource that has a reference to a scene file, such as “res://Scenes/Main.tscn”.
```python
    ### LevelDoor.gd
    
    # Allows us to change the scene (level) we want to go to in the editor properties
    @export var next_level: PackedScene
```
By exporting this resource, we can go back to our level scenes where we instanced our LevelDoor scene, and choose the next level in the Inspector panel! To assign a new scene, select your Instanced LevelDoor in your Main scene and click on the PackedScene property in the inspector panel. Select the option to “Quick Load”. In my Main scene, I’m going to choose my *next-level* resource to be Main_2, and so forth.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*NnlPQriZUSEOlNnHv0PrBA.png)

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*wDrObDESc9as-Gb-l3GdBg.png)

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*s6CRB64I7M0YE_sKCOosqA.png)

If you were to run your scene now, and your player runs into the door collision, the menu should show but the player’s UI will show over it. Let’s fix this issue by hiding the player’s UI layer *on_body_entered().*

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3774/1*k1PI4xjwKXkd_Zjrhtb3lQ.png)
```python
    ### LevelDoor.gd
    
    #older code
    
    func _on_body_entered(body):
        if body.name == "Player":
            # pause game
            get_tree().paused = true
            # show menu
            $UI/Menu.visible = true
            # make modular value visible
            $AnimationPlayer.play("ui_visibility")  
            #hide the player's UI 
            body.get_node("UI").visible = false
```
Now, back to our ButtonContinue function. If our player presses the continue button, we want to change our current scene to our next scene. We will use the *[change_scene_to_packed()](https://docs.godotengine.org/en/stable/classes/class_scenetree.html#class-scenetree-method-change-scene-to-packed)* method, which changes the running scene to a new instance of the given [PackedScene](https://docs.godotengine.org/en/stable/classes/class_packedscene.html#class-packedscene) (which must be valid). In simple terms, this will change our current scene (Main) to our packed scene chosen in the editor (Main_2).

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/1*8okZg3Q5oLtS0Y-haNyEvg.png)
```python
    ### LevelDoor.gd
    
    #older code
    
    func _on_continue_button_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $UI/Menu.visible = false
        # Change to the next scene
        get_tree().change_scene_to_packed(next_level)
```
Because we saved the name of our current scene in our Global script, we’ll have to do some magic to get the name of our Packed scene. This is because our *current_scene* is being saved as “Main” or “Main_2”, whilst our *next_level* is being saved as <PackedScene#-9223371970517660679>. This is because it is saving it as a scene resource, and not a scene file. To get the scene file path from our scene resource, we’ll need to extract our path via the *[resource_path](https://docs.godotengine.org/en/stable/classes/class_resource.html#class-resource-property-resource-path)* method. This path can be an absolute or a relative file path.

```python   
    ### LevelDoor.gd
    
    #older code
    
    func _on_continue_button_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $UI/Menu.visible = false
        # Change to the next scene
        get_tree().change_scene_to_packed(next_level)
        # gets the path of our packed scene resource
        var path = next_level.resource_path
```
From this path, we can get the filename of the path. We will use the *[get_file()](https://docs.godotengine.org/en/stable/classes/class_string.html#class-string-method-get-file)* method for this, which returns the file name and extension of our path. For example, if the path is “res://folder/file.png”, path.get_file() would give you “file.png”.

Then, we’ll need to split our filename into an array, which will break apart our filename so that we can get the name value away from its extension. For example, if the file name is “file.png”, split(“.”) would give you an array like this: [“file”, “png”].

We’ll then access this filename via the array index [0]. All of this together will transform **<PackedScene#…>** into **“res://Scenes/Main_2.tscn”** into **“Main_2”**, which we can then assign to our current_scene variable.
```python
    ### LevelDoor.gd
    #older code
    
    func _on_continue_button_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $UI/Menu.visible = false
        # Change to the next scene
        get_tree().change_scene_to_packed(next_level)
        # Extract the name of the packed scene file and update the current scene's name in the Global script
        var path = next_level.resource_path
        var scene_name = path.get_file().split(".")[0]
        Global.current_scene_name = scene_name
```
Now if you run your scene, and you enter your door at the top — the menu should show. If you press the continue button, your level should change to the next level — and so forth.

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*1TH_Qfh7zLV-WcC1tV7ECg.png)

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*Dm0MJWKwRVN16MPRGgYfpA.png)

With the restart button, we want to reload the *current_scene* that the player is in — which in our case is our Main scene. We can simply use the *[reload_current_scene() ](https://docs.godotengine.org/en/stable/classes/class_scenetree.html#class-scenetree-method-reload-current-scene)* method which reloads the currently active scene.
```python
    ### LevelDoor.gd
    
    #older code
    
    func _on_restart_button_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $UI/Menu.visible = false
        # Restart current scene
        get_tree().reload_current_scene()
```
![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*AGsbrWXGubfYlxyqYC-Rww.png)

You will see from my screenshots above that my player returns a score, time, and ranking value. When our level ends, we want to display the time in seconds that the player took to complete the level. We also want to show their final score obtained. Then, our ranking system will be determined based on their final score and final time. If they took less than 1 minute and got 10000 points on their score, they get the “Master” ranking. This ranking system is not similar to the Donkey Kong system at all — which uses a pacing system. You can read more about that [here.](http://donkeykongblog.blogspot.com/2012/11/understanding-pace.html#:~:text=You%20get%20points%20in%20Donkey,timer%20upon%20clearing%20the%20board.)

![Figure 11: Our Game’s Ranking System](https://cdn-images-1.medium.com/max/5048/1*RCyzkzYJKHYvqxdVajIuuA.png)
>_Figure 11: Our Game’s Ranking System_

In our Global script, let’s create three new variables that will store our final score, final rating, and final time.
```python
    ### Global.gd
    
    #older code
    
    #final level results
    var final_score
    var final_rating
    var final_time
```
In our LevelDoor script, we will display the values of these global variables in our UI Value nodes. These won’t show anything but “0” yet since we haven’t created a function that will capture the values of these variables yet.
```python
    ### LevelDoor.gd
    
    #older code
    
    func _on_body_entered(body):
        if body.name == "Player":
            # pause game
            get_tree().paused = true
            # show menu
            $UI/Menu.visible = true
            # make modular value visible
            $AnimationPlayer.play("ui_visibility")  
            #hide the player's UI 
            body.get_node("UI").visible = false 
            # show player values
            $UI/Menu/Container/TimeCompleted/Value.text = str(Global.final_time)
            $UI/Menu/Container/Score/Value.text = str(Global.final_score)
            $UI/Menu/Container/Ranking/Value.text = str(Global.final_rating)
```
To get our final score, we will store the value of our player’s score value in a new function which will return the score, ranking, and time.
```python
    ### Player.gd
    
    #older code
    
    func final_score_time_and_rating():
        # Final results
        Global.final_score = score
```
To get the time taken, we first need to create a new variable that will capture the time of the system when we started the game. We can get the time using the [Time ](https://docs.godotengine.org/en/stable/classes/class_time.html)object, which has a method called [get_ticks_msec()](https://docs.godotengine.org/en/stable/classes/class_time.html#class-time-method-get-ticks-msec). This method returns the amount of time passed in milliseconds since the engine started.
```python
    ### Player.gd
    
    #older code
    
    #time we started the level
    var level_start_time = Time.get_ticks_msec()
```
In our *final_score_time_and_rating()* function, we will use the Time method again to get the time when the level ended, which we will deduct from our time started to get the completion time. From this completion time value, we will time it by 1000 to convert it into a seconds value.
```python
    ### Player.gd
    
    #older code
    
    func final_score_time_and_rating():
        # Time to complete in seconds
        var time_taken = (Time.get_ticks_msec() - level_start_time) / 1000.0 # Convert to seconds
        
        # Final results
        Global.final_score = score
```
Since we don’t want the time to show with multiple decimals (such as 4.123974918371), we’ll round this value before storing it as our final time value.
```python
    ### Player.gd
    
    #older code
    
    func final_score_time_and_rating():
        # Time to complete in seconds
        var time_taken = (Time.get_ticks_msec() - level_start_time) / 1000.0 # Convert to seconds
        var time_rounded = str(roundf(time_taken)) + " secs"
        
        # Final results
        Global.final_score = score
        Global.final_time = time_rounded
```    
To get the ranking, we will create a temporary variable that will return a string value based on the time and score captured. The image below gives a good demonstration of how our ranking system works. You can change these values to whichever amounts you want, but just make sure that you give your player enough opportunities (such as score and attack boost pickups, and obstacles to jump over) to get *x* amount of points for *n* rating!

```python
    ### Player.gd

    #older code
    
    func final_score_time_and_rating():
        # Time to complete in seconds
        var time_taken = (Time.get_ticks_msec() - level_start_time) / 1000.0 # Convert to seconds
        var time_rounded = str(roundf(time_taken)) + " secs"
        # Rating based on time and score
        var rating = ""
        
        if time_taken <= 60 and score >= 10000:
            rating = "Master" # Exceptionally high score and fast completion
        elif time_taken <= 120 and score >= 5000:
            rating = "Pro" # Very high score and fast completion
        elif time_taken <= 180 and score >= 3000:
            rating = "Expert" # High score and moderately fast completion
        elif time_taken <= 240 and score >= 2000:
            rating = "Intermediate" # Good score and completion time
        elif time_taken <= 300 and score >= 1000:
            rating = "Amateur" # Decent score, but not very fast
        else:
            rating = "Beginner" # All other cases
        
        # Final results
        Global.final_score = score
        Global.final_time = time_rounded
        Global.final_rating = rating
```
Now, if we were to run our scene, our final values will still display as “0” since we haven’t called our *final_score_time_and_rating()* function in our LevelDoor scene yet — hence no values have been captured!
```python
    ### LevelDoor.gd
    
    #older code
    
    func _on_body_entered(body):
        if body.name == "Player":
            # pause game
            get_tree().paused = true
            # show menu
            $UI/Menu.visible = true
            # make modular value visible
            $AnimationPlayer.play("ui_visibility")  
            #hide the player's UI 
            body.get_node("UI").visible = false 
            #get final values
            body.final_score_time_and_rating()
            # show player values
            $UI/Menu/Container/TimeCompleted/Value.text = str(Global.final_time)
            $UI/Menu/Container/Score/Value.text = str(Global.final_score)
            $UI/Menu/Container/Ranking/Value.text = str(Global.final_rating)
```
Your code should look like [this](https://github.com/christinec-dev/CastleClimbersParts/tree/main/Part16).

Now if you run your scene and you make it to the top, your score, time, and ranking value should show! You should also be allowed to restart your game and continue to the next level. Please note that I cheated my score value in the code itself for testing purposes, so I am in no way a “Master” ranking player!

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*1TH_Qfh7zLV-WcC1tV7ECg.png)

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/3840/1*OyAm4SKquYC_ik8EalZP8Q.png)

It’s easy to progress to the next level and get x amount of score points if your player never dies. Currently, our player’s lives can be depleted and we can still go ahead and gain score points. We need to stop this tomfoolery by giving our players a Death/Game Over screen! In the next part, we’ll implement this feature to stop our game if our player’s health runs out, which will force our player to replay the current level.

Since this part is running quite long already, we will break off here and continue in the next part where we create the UI items to show our Level value and time count on our screen. This way the player knows which level they are in, and they know what their time value is currently at.

Now would be a good time to save your project and make a backup of your project so that you can revert to this part if any game-breaking errors occur. Go back and revise what you’ve learned before you continue with the series, and once you’re ready, I’ll see you in the next part!

---

## Next Part to the Tutorial Series

The tutorial series has 24 chapters. I’ll be posting all of the chapters in sectional daily parts over the next couple of weeks. You can find the updated list of the tutorial links for all 24 parts of this series on my [GitBook](https://oops-i-devd.gitbook.io/christinedevs/godot-tutorials/castle-climbers/tutorial). If you don’t see a link added to a part yet, then that means that it hasn’t been posted yet. Also, if there are any future [updates ](https://oops-i-devd.gitbook.io/christinedevs/communication/updates-and-notifications)to the series, my GitBook would be the place where you can keep up-to-date with everything!

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/0*TLFhoNqnjMYLWAGA.png)

---

## Support the Series & Gain Early Access!

If you like this series and would like to support me, you could [donate](https://ko-fi.com/christinedevs) any amount to my KoFi shop or you could [purchase the offline PDF](https://ko-fi.com/s/75433f1d4b) that has the entire series in one on-the-go booklet!

The booklet gives you lifelong access to the full, offline version of the “**Learn Godot 4 by Making a 2D Platformer**” PDF booklet. This is a 451-page document that contains all the tutorials of this series in a sequenced format, plus you get dedicated help from me if you ever get stuck or need advice. This means you don’t have to wait for me to release the next part of the tutorial series on Dev.to or Medium. You can just move on and continue the tutorial at your own pace — anytime and anywhere!

![Godot 2D Platformer](https://cdn-images-1.medium.com/max/2000/0*S6n1i41QDrVlLJUy.png)