Create Your First Godot 4 Game
Lesson 6: Creating a collectible
Create Your First Godot 4 Game
- Lesson 1: Getting started with Godot 4 2:53
- Lesson 2: Overview of the Godot interface 18:24
- Lesson 3: Setting up the level 11:09
- Lesson 4: Creating the player 22:42
- Lesson 5: Spawning the platforms 8:09
- Lesson 6: Creating a collectible
- Lesson 7: Creating an enemy 10:46
- Lesson 8: Handling player death 10:26
- Lesson 9: Creating the player projectile 13:05
- Lesson 10: Adding the final touches 6:54
- Lesson 11: Exporting your game and wrapping up 2:37
Video Notes
Let’s give our raptor some treats! In this lesson, we’ll cover creating a collectible bug that our dino can grab to score some points.
The final code can be found here: https://github.com/quiver-dev/raptor-run.
Corrections
In Godot 4 Beta 16 or higher, the AnimatedSprite2D’s Playing checkbox has been removed from the Inspector. It has been replaced by the Autoplay button, which can be found in SpriteFrames panel next to the trashcan icon.
Make sure to turn off looping on the collected
animation, otherwise the animation_finished signal will never be fired for the collectible and it will never be freed.
Improvements
With newer versions of Godot 4, you can simplify the platform selection with the following:
var random_platform = available_platforms.pick_random()
-
strawfervor
Dec. 15, 2022 at 9:31 p.m.This could be very nice tutorial, but it’s way too fast. It is impossible to follow you in this course, because you are pasting things, so it’s very frustraiting to stop or rewind it all the times to catch you up. In this lesson you forgot to mention about line of code with collect_sound varible (9 minute).
-
amit
Dec. 16, 2022 at 7:01 p.m.Thanks for the feedback. It’s difficult to find the pacing that suits everyone, but I agree we should avoid copy and pasting multiple lines and that’s something we’ll address in future tutorials. Speaking for myself, I find myself pausing the video even if I can keep up with the writing the code, since it often takes a moment for the idea to process.
I believe the
collect_sound
variable is initialized at 8:30 in the video. Is that what you mean?
-
-
Malcx
Feb. 12, 2023 at 12:31 p.m.Why do we set the label text on every frame rather than just when the score changes?
And would it not be more performant to create the available_platforms array once rather than every spawn call?
-
amit
Feb. 24, 2023 at 7:13 p.m.Both valid points. I think the best way to handle updating the score label would be using a setter function on the
score
variable, which we don’t cover in this tutorial. Andavailable_platforms
could certainly be initialized once.
-
-
ManoShu
Oct. 20, 2023 at 2:02 a.m.A thing that could be done a little bit better is to put a clamp on the collectible_pitch variable, as, in the case of the game spawning multiple platforms with “row” collectibles, the sound becomes jarring.
After the
collectible_pitch += 0.1
line, add:collectible_pitch = clamp(collectible_pitch, 1.0, 2.0)
-
ManoShu
Oct. 20, 2023 at 2:08 a.m.The increase on the
collectible_pitch
can also be reduced to have a similar effect while not using a high pitch value:collectible_pitch += 0.05 # or other lower values
-
amit
Oct. 20, 2023 at 3:10 p.m.Great idea! This would be especially useful if you’re designing custom platforms with even more collectibles.
-
-
RedHoodJT
March 15, 2024 at 12:07 a.m.For some reason when I run the game my animation for picking up the collectible runs in a loop over and over again and the last collectible becomes hidden behind the platform but I am still able to collect it. Here is my code for the collectible:
extends Area2D @export var value = 10 @onready var game = $"/root/World" @onready var sprite = $AnimatedSprite2D # Called when the node enters the scene tree for the first time. func _ready(): self.body_entered.connect(_on_body_entered) func _on_body_entered(body): if body.is_in_group("player"): game.add_score(value) sprite.play("collected") sprite.animation_finished.connect(_on_animation_finished) func _on_animation_finished(): queue_free() func _process(delta): pass
I have gone back and looked at the code in the lecture to what I have but I didn’t notice anything different. I tried deleting the animation and recreating it but that didn’t seem to do anything. I feel like I missed a step in the code that removes it from the scene.
-
RedHoodJT
March 15, 2024 at 12:44 a.m.Never mind. I forgot to disable looping.
-
aainflight
April 15, 2024 at 2:29 a.m.cool
-
cswalker
May 16, 2024 at 7:51 p.m.Collision is not triggering for me on
Godot Engine v4.2.2.stable.flathub [15073afe3]
Player - CharacterBody2D is in a group named
player
and is on Collision Layer 2. Everything looks setup correctly as everything up till this point works as the guide states.Collectible - Area2D has AnimatedSprite2D & CollisionShape2D (including looping off in the animation frames for the collected animation). It is also on Collision Layer 2.
I put some print statements to help identify in the Output where something isn’t working. On play, the first print statement in
func _ready()
printscollectible ready
in the output stream. However, the second print statement infunc _on_body_entered(body)
never printsbody entered
. So, this leads me to believe that theself.body_entered.connect(_on_body_entered)
statement never triggers.I first thought it a problem with
body_entered
but the documents state it can be aPhysicsBody2D
whichCharacterBody2D
is inherited by. I also, thought that this might only trigger on the first frame since it is in the_ready()
function, but this seem not to be the case either. Or if it is, I don’t know where it should go.NOTE: My game variable points to World_Node2D instead of World, because I named it that to help me with my learning.
@export var value = 10 @onready var game = $"/root/World_Node2D" @onready var sprite = $AnimatedSprite2D # Called when the node enters the scene tree for the first time. func _ready(): print("collectible ready") self.body_entered.connect(_on_body_entered) func _on_body_entered(body): print("body entered") if body.is_in_group("player"): game.add_score(value) sprite.play("collected") sprite.animation_finished.connect(_on_animation_finished) func _on_animation_finished(): queue_free() # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): pass
-
amit
May 17, 2024 at 3:32 p.m.Can you post your code somewhere and send a link? I can take a look at it once you do.
-
cswalker
May 19, 2024 at 6:22 p.m.Thanks for offering to help. I found it was the collision mask after reading some docs. I’m trying to scan through now to find if i missed it on the player or the collectible it the guide, but not finding the mask portion.
-
Psych0Dad
Sept. 14, 2024 at 12:18 p.m.I had the same issue. my problem was, that my CollisionShape2D node was a child of AnimatedSprite2D and not Collectible.
-
-
-
-
euvisio
Dec. 24, 2024 at 10:14 a.m.In Godot 4.3 under Node Groups, there are Scene Groups and Global Groups. Should our Player be made part of the Global or Scene Group?
Anoniempje
Nov. 7, 2022 at 7:55 p.m.As of godot 4 beta 4 there is a random pick from array function.
At 15m55s, line 42 and 43 can now be:
amit
Nov. 8, 2022 at 12:14 a.m.Nice find! A small, but welcome addition to GDScript.