Skip to content

Commit f11d33b

Browse files
committed
Add leveling system demo for Godot 4.5
1 parent 3f6d4f9 commit f11d33b

22 files changed

+393
-35
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
# Demo: Savegames in Godot 4
1+
# Demo: Leveling System in Godot 4
22

3-
This project gives an example of how to save and load a game's state in Godot using resources and JSON.
3+
This project gives an example of how to make an RPG-style leveling system in Godot 4.
44

5-
For a detailed guide to get you started with saving and loading in Godot, check out the GDQuest Library: [Saving and loading games in Godot 4 (with resources)](https://gdquest.com/library/save_game_godot4/)
5+
<!-- TODO: add link to library guide -->
6+
For a detailed guide to get you started with saving and loading in Godot, check out the GDQuest Library: []()

character.gd

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
extends Node
2+
3+
# `growth_data` gets passed to the experience progress bar. The first value of the array
4+
# represents the value we animate the bar to, while the second is the maximum experience
5+
# value that the bar represents.
6+
signal experience_gained(growth_data: Array)
7+
8+
# Character stats
9+
@export var max_hp := 12
10+
@export var strength := 8
11+
@export var magic := 8
12+
13+
# Leveling system
14+
@export var level := 1
15+
16+
var experience := 0
17+
var experience_total := 0
18+
var experience_required := get_required_experience(level + 1)
19+
20+
21+
func get_required_experience(for_level: int) -> int:
22+
return roundi(pow(for_level, 1.8) + for_level * 4)
23+
24+
25+
func gain_experience(amount: int) -> void:
26+
experience_total += amount
27+
experience += amount
28+
var growth_data := []
29+
while experience >= experience_required:
30+
experience -= experience_required
31+
# In case that the amount of experience gained so far is greater than the required
32+
# experience to level up, we need to fill the experience bar to the max. We repeat
33+
# this process as long as `experience` is greater or equal with `epxerience_required`.
34+
growth_data.append([experience_required, experience_required])
35+
level_up()
36+
growth_data.append([experience, experience_required])
37+
experience_gained.emit(growth_data)
38+
39+
40+
func level_up() -> void:
41+
level += 1
42+
experience_required = get_required_experience(level + 1)
43+
44+
var stats: Array[String] = ["max_hp", "strength", "magic"]
45+
var random_stat := stats[randi() % stats.size()]
46+
set(random_stat, get(random_stat) + randi() % 4)

character.gd.uid

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://wdiq5bld7b1f

fonts/Montserrat-Medium.ttf

188 KB
Binary file not shown.

fonts/Montserrat-Medium.ttf.import

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[remap]
2+
3+
importer="font_data_dynamic"
4+
type="FontFile"
5+
uid="uid://0ryos52rkkfa"
6+
path="res://.godot/imported/Montserrat-Medium.ttf-e832861e4ad4110e172112dc430c04b0.fontdata"
7+
8+
[deps]
9+
10+
source_file="res://fonts/Montserrat-Medium.ttf"
11+
dest_files=["res://.godot/imported/Montserrat-Medium.ttf-e832861e4ad4110e172112dc430c04b0.fontdata"]
12+
13+
[params]
14+
15+
Rendering=null
16+
antialiasing=1
17+
generate_mipmaps=false
18+
disable_embedded_bitmaps=true
19+
multichannel_signed_distance_field=false
20+
msdf_pixel_range=8
21+
msdf_size=48
22+
allow_system_fallback=true
23+
force_autohinter=false
24+
modulate_color_glyphs=false
25+
hinting=1
26+
subpixel_positioning=4
27+
keep_rounding_remainders=true
28+
oversampling=0.0
29+
Fallbacks=null
30+
fallbacks=[]
31+
Compress=null
32+
compress=true
33+
preload=[]
34+
language_support={}
35+
script_support={}
36+
opentype_features={}

game.gd

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
extends Node
2+
3+
@onready var _character := %Character
4+
@onready var _label: Label = %Label
5+
@onready var _experience_bar: TextureProgressBar = %ExperienceBar
6+
7+
8+
func _ready() -> void:
9+
_label.update_text(_character.level, _character.experience, _character.experience_required)
10+
_experience_bar.initialize(_character.experience, _character.experience_required)
11+
12+
13+
func _unhandled_input(event: InputEvent) -> void:
14+
if not event.is_action_pressed("ui_accept"):
15+
return
16+
17+
# We just picked a random value to increase the experience by. Notice how the higher level
18+
# need you to run this function multiple times before leveling up.
19+
_character.gain_experience(34)
20+
_label.update_text(_character.level, _character.experience, _character.experience_required)

game.gd.uid

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://bor8mhwft1id0

game.tscn

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
[gd_scene load_steps=6 format=3 uid="uid://bmekn0vlbo50k"]
2+
3+
[ext_resource type="Script" uid="uid://bor8mhwft1id0" path="res://game.gd" id="1_7jktm"]
4+
[ext_resource type="Script" uid="uid://wdiq5bld7b1f" path="res://character.gd" id="2_ryrav"]
5+
[ext_resource type="PackedScene" uid="uid://c1yrk33055lj5" path="res://interface/experience_bar.tscn" id="3_eow3j"]
6+
[ext_resource type="FontFile" uid="uid://0ryos52rkkfa" path="res://fonts/Montserrat-Medium.ttf" id="4_j5wjh"]
7+
[ext_resource type="Script" uid="uid://b7mjuigbuvay8" path="res://label.gd" id="5_vef74"]
8+
9+
[node name="Game" type="Node"]
10+
script = ExtResource("1_7jktm")
11+
12+
[node name="Character" type="Node" parent="."]
13+
unique_name_in_owner = true
14+
script = ExtResource("2_ryrav")
15+
16+
[node name="InterfaceControl" type="Control" parent="."]
17+
custom_minimum_size = Vector2(0, 120)
18+
layout_mode = 3
19+
anchors_preset = 15
20+
anchor_right = 1.0
21+
anchor_bottom = 1.0
22+
grow_horizontal = 2
23+
grow_vertical = 2
24+
25+
[node name="ColorRect" type="ColorRect" parent="InterfaceControl"]
26+
layout_mode = 0
27+
anchor_right = 1.0
28+
anchor_bottom = 1.0
29+
color = Color(0.238983, 0.251691, 0.320313, 1)
30+
31+
[node name="ExperienceBar" parent="InterfaceControl" instance=ExtResource("3_eow3j")]
32+
unique_name_in_owner = true
33+
layout_mode = 1
34+
anchors_preset = -1
35+
anchor_left = 0.5
36+
anchor_top = 0.525
37+
anchor_right = 0.5
38+
anchor_bottom = 0.525
39+
offset_left = -430.5
40+
offset_top = -34.5
41+
offset_right = 430.5
42+
offset_bottom = 34.5
43+
grow_horizontal = 2
44+
grow_vertical = 2
45+
value = 28.0
46+
47+
[node name="Label" type="Label" parent="InterfaceControl"]
48+
unique_name_in_owner = true
49+
modulate = Color(0.921569, 0.533333, 0.529412, 1)
50+
layout_mode = 0
51+
offset_left = 80.0
52+
offset_top = 80.0
53+
offset_right = 314.0
54+
offset_bottom = 249.0
55+
theme_override_fonts/font = ExtResource("4_j5wjh")
56+
theme_override_font_sizes/font_size = 32
57+
text = "Level: 4
58+
Experience: 86
59+
Next level: 23
60+
"
61+
script = ExtResource("5_vef74")
62+
63+
[connection signal="experience_gained" from="Character" to="InterfaceControl/ExperienceBar" method="_on_character_experience_gained"]

icon.png

3.12 KB
Loading

icon.png.import

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[remap]
2+
3+
importer="texture"
4+
type="CompressedTexture2D"
5+
uid="uid://dny5vvkk2j0nu"
6+
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
7+
metadata={
8+
"vram_texture": false
9+
}
10+
11+
[deps]
12+
13+
source_file="res://icon.png"
14+
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
15+
16+
[params]
17+
18+
compress/mode=0
19+
compress/high_quality=false
20+
compress/lossy_quality=0.7
21+
compress/uastc_level=0
22+
compress/rdo_quality_loss=0.0
23+
compress/hdr_compression=1
24+
compress/normal_map=0
25+
compress/channel_pack=0
26+
mipmaps/generate=false
27+
mipmaps/limit=-1
28+
roughness/mode=0
29+
roughness/src_normal=""
30+
process/channel_remap/red=0
31+
process/channel_remap/green=1
32+
process/channel_remap/blue=2
33+
process/channel_remap/alpha=3
34+
process/fix_alpha_border=true
35+
process/premult_alpha=false
36+
process/normal_map_invert_y=false
37+
process/hdr_as_srgb=false
38+
process/hdr_clamp_exposure=false
39+
process/size_limit=0
40+
detect_3d/compress_to=1

0 commit comments

Comments
 (0)