Use this space for questions related to what you're learning. For any other type of support (website, learning platform, payments, etc...) please get in touch using the contact form.
Buggy shadow tween due to missing pivotsNPGameDevHey Nathan,
I was trying to create the code for the bird hopping and, due to not setting the pivot correctly, the shadow was shifting "weirdly" because it was losing its local position offset relative to the bird.
The same behaviour is there for the script provided above, so I'd assume it is intentional to use the tween that way, without having to track the position offset manually but instead using the Sprite2D > Offset property when setting it up in the scene.
I was a bit confused for a while until looking into the solution from the completed lessons. Looking at the hints for a fix to the shadow issue didn't help, as there was no mention of said necessary offset, that's why I ended up addressing it via code on my solution, until I realized about it in the solution scene.
As a suggestion, could you add a Hint or something that points towards the Offset as a requirement for the setup? Otherwise, it is easy to miss it and the script will make the shadow look really weird because it's actually written with the assumption of that Offset.
Thanks for all the cool scripts!96Mar. 25, 2024
I challenge yall to tween the bird flipping directions when jumping to the rightShaneIf anyone's interested I think this is a pretty simple extra challenge, try animating the bird flipping to face the right when jumping to the right, and switching back to facing left when jumping left.
The solution I came up with is:
`var tween = create_tween()`
`if land_position.x > position.x:`
`tween.tween_property(sprite2d, "scale:x", -1, 0.3)`
`else:`
`tween.tween_property(sprite2d, "scale:x", 1, 0.3)`295Apr. 04, 2024
Awesome Challenge!AJ Studios"Making a bird that hops around" has been a great challenge. In the end I had to look at the code since I couldn't get it right on my own however, that made me read the code several times and it helped me make more sense of how it works. I'm looking forward to more challenges like this one. 83Apr. 12, 2024
How to Add Item Clicked Counter?Owl KnightSince the item script is shared between randomly instantiated array scenes, which themselves are nested decently far from the main scene (mini_dungeon), I am a bit confused. How can I connect a signal from the item.gd script to the mini_dungeon scene?
I am assuming/using a setup where I have added a Label as a child of mini_dungeon and want to update the text within to display the current count of items clicked
MiniDungeon
`extends Node2D`
`var count: int = 0`
`@onready var item_count: Label = $ItemCount`
`func _on_add_count() -> void:`
` count += 1`
` item_count.text = str(count)`32Jul. 11, 2024
My birds seem to get yeeted to a specific position before hoppingMartinBTried to set this up so that my birds at random intervals will do a little hop, sometimes they'll hop very soon after hopping and other times they'll wait a bit.
But for some reason before they start hopping, they fly over to the top left, and then start hopping.
`func sparrow_hop() -> void:`
`const FLIGHT_TIME := 0.1`
`const HALF_FLIGHT_TIME := FLIGHT_TIME / 2`
`var random_angle := randf_range(0.0, 2.0 * PI)`
`var random_direction := Vector2(1.0, 0.0).rotated(random_angle)`
`var random_distance := randf_range(1.0, 15.0)`
`var jump_height := 10.0`
`var landing_position := random_direction * random_distance`
`var tween := create_tween().set_parallel()`
`tween.tween_property(sparrow, "position:x", landing_position.x, FLIGHT_TIME)`
`tween = create_tween()`
`tween.tween_property(sparrow, "position:y", landing_position.y - jump_height, HALF_FLIGHT_TIME)`
`tween.tween_property(sparrow, "position:y", landing_position.y, HALF_FLIGHT_TIME)`
`func _on_hop_timer_timeout():`
`sparrow_hop()`
`hop_timer.wait_time = randf_range(0.1, 4.0)`
42Apr. 01, 2024
Sharing script functions across scriptssalty-dinosaurI'm coming from Javascript land where importing and exporting functions is a breeze. Is there something similar in GDScript?
My question is: because I'm re-using the `is_left_click` logic check across multiple scripts, I'd like to see how I could refactor this check into a reusable getter function that I can then do as something like...
```gdscript
var is_left_click = get_was_left_mouse_clicked()
```
This would remove multiple lines of repeated code and ensure the clicking logic only lives in one place.
If I'm understanding Godot correctly, I suppose one solution would be to create a new parent node with this function that is the root for any node I want to be left-clickable?31Sep. 06, 2024
tween property selfMainzelHi there, maybe I didnt read everything right but was there a section where you told us about the self parameter? I tried to do the dissapearing / clicking task with:
tween.tween_property(----, "scale", Vector2(0, 0), 0.3) but had no idea what to insert in ---- until I saw the solution with "self" as the answer. Maybe you could clarify that for me (sry for bad english)51Jun. 07, 2024
Solved it slightly differently..are there any disadvantages to doing it this way?inferior-gnat```gdscript
extends Node2D
@onready var area_2d: Area2D = $Area2D
@onready var timer: Timer = $Timer
@export var min_movement_distance := 60.0
@export var max_movement_distance := 120.0
@export var jump_height := 10.0
@export var anim_duration := 0.4
var flight_anim_duration: float:
get:
return anim_duration / 2
func _ready() -> void:
timer.timeout.connect(wander)
func wander() -> void:
var distance := randf_range(min_movement_distance, max_movement_distance)
var direction := Vector2(1.0, 0).rotated(randf_range(0.0, deg_to_rad(360.0)))
var target_position: Vector2 = direction * distance
var land_position: Vector2 = position + target_position
var tween := create_tween().set_parallel()
tween.tween_property(self, "position", land_position, anim_duration)
tween = create_tween().set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tween.tween_property(area_2d, "position:y", area_2d.position.y - jump_height, flight_anim_duration)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(area_2d, "position:y", area_2d.position.y, flight_anim_duration)
```
This was my solution. I handled the ground movement at the parent level, since the bird and shadow have to move to the same spot anyway. I then just tweened the jump anim on the child Area2D.
Curious whether there are any disadvantages to doing it this way?11Jun. 07, 2024
How to use the offset function?humble-pelicanRegarding the offset of the shadow, I would like to ask how to use this function? Because I did not see the offset function in the solution code provided, my code is written like this, but I would like to know how to write it using the offset function, thank you
```gdscript
var tween := create_tween().set_parallel()
tween.tween_property(sparrow,"position:x",land_position.x,JUMP_DURATION)
tween.tween_property(shadow,"position",Vector2(land_position.x-5,land_position.y+15),JUMP_DURATION)
```21May. 29, 2024
tween confusionStonemonkeyI don't quite get how the tweens work. I tried experimenting but I don't get the results I was expecting.
Here one of the things I was trying to do is move the sprite in a "V" movement. Instead the x movement is being combined so the sprite moves to the 900 position in one movement.
This is part of my confusion with how the animations we have done is split up between the x and y movement.
Does `create_tween()` stop the first `set_parallel` ? So that the next set of movement can happen sequentially for example.
```gdscript
extends Node2D
@onready var sprite_2d: Sprite2D = $Sprite2D
func _ready() -> void:
generic_animation()
func generic_animation():
#The bird sprite is manually placed in the scene at (546, 266)
var tween = create_tween()
tween.set_parallel()
tween.tween_property(sprite_2d, "position:x", 700.0, 2 )
tween.tween_property(sprite_2d, "position:y", 400.0, 2 )
#
#tween = create_tween()
##tween.set_parallel()
#tween.tween_property(sprite_2d, "position:x", 900.0, 2 )
#tween.tween_property(sprite_2d, "position:y", 200.0, 2 )
```31Apr. 22, 2024
Another solution for shadowsTai921```gdscript
extends Node2D
@onready var wait_time : Timer = $Timer
@onready var bird:Sprite2D = $Sprite2D
@onready var Shadow:Sprite2D = $Shadow
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
wait_time.wait_time = randf_range(1.0,3.0)
wait_time.one_shot = true
wait_time.timeout.connect(_on_jump)
wait_time.start()
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_jump() -> void:
const FLIGHT_TIME := 0.4
const HALF_FLIGHT_TIME := FLIGHT_TIME / 2.0
# 取得亂數位置 / Get random position
var random_angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle)
var random_distance := randf_range(5.0, 10.0)
var land_position := random_direction * random_distance
var tween := create_tween().set_parallel()
#鳥移動到哪裡,影子就移動到哪裡 / Wherever the bird moves, the shadow moves
tween.tween_property(bird, "position", land_position, FLIGHT_TIME)
#針對影子進行額外的計算 / Perform additional calculations for shadows
tween.tween_property(Shadow, "position", Vector2(land_position.x - 6.0, land_position.y + 15.0), HALF_FLIGHT_TIME)
#print("land_position.y + shadow_offset.y = " , land_position.y + shadow_offset.y)
#讓鳥跳起來 / make the bird jump
const jump_heright = 16.0
tween = create_tween().set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tween.tween_property(bird,"position:y", land_position.y - jump_heright, HALF_FLIGHT_TIME)
tween.tween_property(bird,"position:y", land_position.y, HALF_FLIGHT_TIME)
tween = create_tween().set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tween.tween_property(Shadow, "scale", Vector2(0.5, 0.5), HALF_FLIGHT_TIME)
tween.tween_property(Shadow, "scale", Vector2(1.563, 0.977), HALF_FLIGHT_TIME)
wait_time.wait_time = randf_range(1.0,3.0)
tween.finished.connect(wait_time.start)
pass
```
This is a fun challenge!
11Apr. 20, 2024
Confused about Bird SolutionshadowI think it doesn't really matter because the birds are sort of decoration in this case (and maybe I'm reading it incorrectly) but doesn't the provided solution mean that we're never actually moving the bird ***object*** itself, but only relocating its associated shadow ***sprite*** and bird ***sprite***?
In its current state it seems like the bird object is actually more of a bird controller object where the "bird" is sort of being moved independently of the object that contains it.
What I did instead is I tweened the "offset:y" and the "scale" of the shadow:
```gdscript
var shadow_scale := shadow.scale
var tween := create_tween()
tween.tween_property(self, "position:x", random_spot.x, hop_time)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_parallel()
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(shadow, "offset:y", 18.0 + (hop_height * 2.0), half_hop_time)
tween.tween_property(shadow, "scale", shadow_scale * 0.75, half_hop_time)
tween.tween_property(self, "position:y", random_spot.y - hop_height, half_hop_time)
await tween.finished
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_parallel()
tween.set_ease(Tween.EASE_IN)
tween.tween_property(shadow, "offset:y", 18.0, half_hop_time)
tween.tween_property(shadow, "scale", shadow_scale, half_hop_time)
tween.tween_property(self, "position:y", random_spot.y, half_hop_time)
```
This solution could definitely be improved on, but I think for now it gives me a pretty decent hop and keeps the shadow separate from the body during movement, while also keeping the bird's sprite location tied to the location of the bird object
This would be important if it was an enemy, I think.
Again, I could be totally wrong on this, but this is what I came to.31Apr. 17, 2024
Trouble toggling the flame visibilitysuperior-aardvarkI was able to make the flame invisible. However after adding code to make it visible again after clicking the torch again, clicking the torch does nothing. Might it have something to do with how I set up these if statements?
```gdscript
extends Area2D
@onready var flame: Sprite2D = $Flame
func _ready() -> void:
# This parameter of the shader material gives each flame a slightly different look and randomized animation.
flame.material.set("shader_parameter/offset", global_position * 0.1)
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
var event_is_mouse_click: bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if event_is_mouse_click and flame.visible == true:
flame.visible = false
if event_is_mouse_click and flame.visible == false:
flame.visible = true
```10Nov. 10, 2024
Solution to the UI challenge using ONLY material learned in M1~M6 (except for 2 methods)PurpleSunriseHello, over the past day and a half I struggled over this UI challenge as I wanted to achieve the result using ONLY concepts and skills learned until the end of M6 without using advanced or external techniques. I think I managed!
First of all I created a UI scene with this hierarchy.
```gdscript
Control
CanvasLayer
CanvasGroup
Sprite2D #apples
Label #counter
Sprite2D #keys
Label #counter
Sprite2D #potions
Label #counter
```
I then set all the visuals in a vertical line going down from the top right corner of the screen.
I added the scene to the mini_dungeon scene as a child of the root node.
I then attached a script to the root node.
Without further ado here's the code:
```gdscript
extends Node2D
@onready var apple_label : Label = get_node("UI/CanvasLayer/CanvasGroup/Sprite2D/AppleCount")
@onready var key_label : Label = get_node("UI/CanvasLayer/CanvasGroup/Sprite2D2/KeyCount")
@onready var potion_label : Label = get_node("UI/CanvasLayer/CanvasGroup/Sprite2D3/PotionCount")
var apple_instances := []
var key_instances := []
var potion_instances := []
var apple_count := 0
var key_count := 0
var potion_count := 0
func _ready() -> void:
_update_items_groups()
func _process(delta: float) -> void:
_update_items_groups()
_check_spawned_apples()
_check_spawned_keys()
_check_spawned_potions()
_update_labels()
func _update_items_groups() -> void:
apple_instances = get_tree().get_nodes_in_group("apples")
key_instances = get_tree().get_nodes_in_group("keys")
potion_instances = get_tree().get_nodes_in_group("potions")
func _check_spawned_apples() -> void:
if apple_instances.is_empty():
return
else:
for apple in apple_instances:
if not apple.is_connected("tree_exited", _on_tree_exited):
apple.tree_exited.connect(_on_tree_exited.bind(apple))
func _check_spawned_keys() -> void:
if key_instances.is_empty():
return
else:
for key in key_instances:
if not key.is_connected("tree_exited", _on_tree_exited):
key.tree_exited.connect(_on_tree_exited.bind(key))
func _check_spawned_potions() -> void:
if potion_instances.is_empty():
return
else:
for potion in potion_instances:
if not potion.is_connected("tree_exited", _on_tree_exited):
potion.tree_exited.connect(_on_tree_exited.bind(potion))
func _on_tree_exited(node_that_exited : Area2D):
if node_that_exited.is_in_group("apples"):
apple_count += 1
if node_that_exited.is_in_group("keys"):
key_count += 1
if node_that_exited.is_in_group("potions"):
potion_count += 1
func _update_labels() -> void:
apple_label.text = "x" + str(apple_count)
key_label.text = "x" + str(key_count)
potion_label.text = "x" + str(potion_count)
```
With @onready annotation I accessed the UI labels counters we need to update.
After digging in the documentation a little bit, I found the method `get_nodes_in_group()` which gets all the nodes of a certain group. Unfortunately by itself it doesn't do anything and after some more digging I found out it needs to be called after `get_tree()` function in order to get all the nodes from a certain group from the scene tree (as it's a method of SceneTree). Solved that problem (the first method outside the learned material) I could get all the nodes generated by the spawning system and store them into 3 Arrays (`get_nodes_in_group()` generates an Array of nodes already).
After that, I created 3 functions (I image I could do it with just one function but I didn't spend time improving the code, I was just SO EXCITED it worked!). These functions are for connecting the nodes to the `tree_exited` signal. And here is where I lost a good amount of my mental health! Even tho the node was perfectly connected to the signal, the signal wasn't calling the function! After hours of testing, switching things around, headbutting the wall and so on I decided to give up. As I was closing the game window for the last time, I noticed the error count in the debug console was augmented by 1 after opening a chest. I casually clicked on the error and I found this:
`E 0:00:04:0417 emit_signalp: Error calling from signal 'tree_exited' to callable: 'Node2D::_on_tree_exited': Method expected 1 arguments, but called with 0. <C++ Source> core/object/object.cpp:1200 @ emit_signalp()`
Well, here I really had to look up on the internet because I had no idea of what was going on. I could tell there was something wrong with the signal and the argument of `_on_tree_exited` but couldn't really tell why or what. I didn't find much but I assumed that the function wasn't receiving any argument... WHAT?? So far we connected signals like this:
`node.signal_name.connect(_on_signal_name)`
and it didn't cause any problem. For some mysterious reasons (which I would love to know) the argument wasn't passed to the function! Well turns out that GD doesn't automatically pass as argument to the function of the signal the node that emitted the signal (and I think there are good reasons for that).
So I just started to read one by one every signal's property and function. Then, I found this "callable" thingy, I started to read a bit and found the `bind()` method. Basically what this does is passing the function the argument in the parenthesis `bind(apple)` like this. That solved the issue!!
Figuring that out it was pretty much straightforward. I updated the variables designated to keep count of the collected items with the `_on_tree_exited` function and then created a function to update the labels of the UI, function which has to be in the _process as well since we want to keep the labels updated all the time.
And that is the end of my first *"coding on my own"* adventure. I would be really happy if this could save some poor soul's time and I would be even happier if someone could improve this code so that I could learn some new tricks! I know this is far from perfect and that it's not reusable, but it's achieved only with very basic concepts learned in these first modules.
I am loving the course so far! Please put more challenges like this over the next modules and lessons!! I think they are really important in order to really assimilate the subjects learned in the lessons. Sorry for the super long story!
Thank you!30Oct. 27, 2024
Can't get my head around this. UI challenge (SOLVED POSTED SOLUTION!!)PurpleSunriseAt the moment I am trying to connected a signal tree_exited to a node.
The signal seems to be connected, but when the node is queue_free() because I click on it, godot doesn't seem to run the function `_on_tree_exited(node_that_exited)`
Can anyone help me understand why is it?
Here's the code I written so far.
```gdscript
extends Node2D
@onready var apple_label : Label = get_node("UI/CanvasLayer/CanvasGroup/Sprite2D/AppleCount")
@onready var key_label : Label = get_node("UI/CanvasLayer/CanvasGroup/Sprite2D2/KeyCount")
@onready var potion_label : Label = get_node("UI/CanvasLayer/CanvasGroup/Sprite2D3/PotionCount")
var apple_instances := []
var key_instances := []
var potion_instances := []
var apple_count := 0
var key_count := 0
var potion_count := 0
var checked : bool = false
func _ready() -> void:
_update_items_groups()
func _process(delta: float) -> void:
_update_items_groups()
_check_spawned_items()
_situation_check()
func _update_items_groups() -> void:
apple_instances = get_tree().get_nodes_in_group("apples")
key_instances = get_tree().get_nodes_in_group("keys")
potion_instances = get_tree().get_nodes_in_group("potions")
func _check_spawned_items() -> void:
if apple_instances.is_empty():
return
else:
for apple in apple_instances:
if not apple.is_connected("tree_exited", _on_tree_exited):
apple.tree_exited.connect(_on_tree_exited)
if apple.is_connected("tree_exited", _on_tree_exited):
print("connected")
func _on_tree_exited(node_that_exited):
print("free")
func _situation_check():
if Input.is_action_just_pressed("ui_accept"):
print(apple_instances)
return
```
I know it's not the best but I just wanted to check if it works before adding more steps.
The script is attached to the dungeon root node.
on tree exited i want to check the item group and add 1 to the designated counter. After that I would code a function that updates the counter label.20Oct. 27, 2024
How to change the order of rendering in the gameram876How can godot make an object with a larger `y` coordinate be drawn on top of an object with a smaller `y` coordinate? For example, no matter in which queue I added chests and birds to the scene, objects lower in position on the screen are drawn higher than those higher on the screen and they can change their position during the game and the order of rendering changes accordingly. Would it be right to use such a simple code?
```gdscript
z_index = position.y
```20Oct. 26, 2024
Solution for bird hoppingPurpleSunriseHello, here's my solution for the bird hopping. Both bird and shadow transform set to 0,0 but shadow offset -3, 17
The bird is hopping really nice. I could generate a randf_range() for the hop height but I liked it to have consistent so I just put a set value.
I would love to know if this code is actually efficient (taking in account the progress so far, I didn't want to check hints or solutions I just used things learned so far).
Thank you!
```gdscript
extends Node2D
@onready var sprite_2d: Sprite2D = $Sprite2D
@onready var shadow: Sprite2D = $Shadow
@onready var timer: Timer = $Timer
var is_hopping : bool = false
func _ready() -> void:
timer.timeout.connect(_on_timer_timeout)
func _on_timer_timeout():
if is_hopping == false:
bird_animation()
func bird_animation() -> void:
is_hopping = true
const HOP_TIME := 0.5
const HALF_HOP_TIME := HOP_TIME / 2
var hop_height := 10.0
var random_angle := randf_range(0.0, 2* PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle).normalized()
var random_distance := randf_range(10.0, 30.0)
var land_position := random_direction * random_distance
tween := create_tween()
tween.set_parallel()
tween.tween_property(sprite_2d, "position:x", land_position.x, HOP_TIME)
tween.tween_property(shadow, "position", land_position, HOP_TIME)
tween = create_tween()
# tween.set_trans(Tween.TRANS_QUAD) I didn't notice much difference so I left it out
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(sprite_2d, "position:y", land_position.y - hop_height, HALF_HOP_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(sprite_2d, "position:y", land_position.y, HALF_HOP_TIME)
is_hopping = false
```40Oct. 26, 2024
Spent ages trying to do somethingscientific-tarsierI just spent something like half an hour trying to figure out how to count up the items into a little text on a control overlay on the mini_dungeon node and couldn't figure out the signalling. Came down to the answers to try and get help and found out we weren't making them collectable, we were making them disappear. :-(10Oct. 23, 2024
Issue with selecting top most itemHappyHi there, I've tried to work in the code:
get_viewport().physics_object_picking_sort = true
get_viewport().physics_object_picking_first_only = true
To make the top most item first selected but I run into a few errors that I can't quite understand. And the problem code seems to be the 'physics_object_picking_first_only' line.
It says in Stack Trace:
Invalid set index 'physics_object_picking_first_only' (on base: 'Window') with value of type 'bool'
And then in the Errors panel it says:
The parameter "viewport" is never used in the function "input_event()". If this is intended, prefix it with an underscore: "_viewport".
I tried to add underscores to where it mentions but that leads to more errors.
20Oct. 16, 2024
label changing codeaeishabad```gdscript
extends Area2D
@onready var apple_count: Label = $"../UI/AppleCount"
@onready var key_count: Label = $"../UI/KeyCount"
@onready var potion_count: Label = $"../UI/PotionCount"
@onready var key: Area2D = $"."
@onready var potion: Area2D = $"."
@onready var apple: Area2D = $"."
var num_potion := 0
var num_key := 0
var num_apple := 0
func _on_tween_finish() -> void :
queue_free()
func set_key_count(num: int):
num_key += num
key_count.text = "x" + str(num_key)
func set_potion_count(num: int):
num_potion += num
potion_count.text = "x" + str(num_potion)
func set_apple_count(num: int):
num_apple += num
apple_count.text = "x" + str(num_apple)
func _ready() -> void:
play_floating_animation()
get_viewport().physics_object_picking = true
get_viewport().physics_object_picking_first_only = true
func shrink_and_delete() -> void:
var tween = create_tween()
tween.set_ease(Tween.EASE_IN)
tween.tween_property($Sprite2D, "scale", Vector2(0,0), 0.2)
tween.finished.connect(_on_tween_finish)
func collect() -> void:
shrink_and_delete()
if potion.is_in_group("potion"):
set_potion_count(1)
elif key.is_in_group("key"):
set_key_count(1)
else:
set_apple_count(1)
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
var _is_clicked: bool = (
event is InputEventMouseButton
and event.button_index == MOUSE_BUTTON_LEFT
and event.is_pressed())
if _is_clicked:
collect()
func play_floating_animation() -> void:
var tween := create_tween()
tween.set_loops()
tween.set_trans(Tween.TRANS_SINE)
var sprite_2d := get_node("Sprite2D")
var position_offset := Vector2(0.0, 10.0)
var duration = randf_range(0.6, 1.4)
sprite_2d.position = -1.0 * position_offset
tween.tween_property(sprite_2d, "position", position_offset, duration)
tween.tween_property(sprite_2d, "position", -1.0 * position_offset, duration)
```
I was able to do the other challenges just fine, but I am unsure on how to to the label
change when an item is collected from the chest, here is what I tried but it
states an "Invalid access to property or key "text" with value of type "string"
on a base object of type "null instance". The script seems to correctly identify
what item was clicked, but is unable to change the label value after the fact. The
labels are children of the MiniDungeon scene.
10Oct. 16, 2024
My hopping Birdsilver-foxHi, first I just want to say this Methode of the curse is great! At first I was a little concern. But really great job! and thank you for the dedication and the answers given.
Now about my attempt to make the bird hop :) I have trouble to get the shadows movement right. When the movement too long is, the shadow makes a clumsy movement. And I can't find the problem
`extends Node2D`
`@onready var sprite_2d: Sprite2D = $Sprite2D`
`@onready var timer: Timer = $Timer`
`@onready var shadow: Sprite2D = $Shadow`
`func hopping() -> void:`
` var random_angle := randf_range(0.0, 2.0 * PI)`
` var random_direction := Vector2(1.0, 0.0).rotated(random_angle)`
` var random_distance := randf_range(30.0, 40.0)`
` var land_position = random_direction * random_distance`
``
` const FLIGHT_TIME := 0.4`
` const HALF_FLIGHT_TIME := FLIGHT_TIME / 2.0`
``
` var tween = create_tween()`
` tween.set_loops()`
` tween.set_parallel()`
` tween.tween_property(sprite_2d, "position:x", land_position.x, FLIGHT_TIME)`
` tween.tween_property(shadow, "position:x", land_position.x - 9, FLIGHT_TIME)`
``
` tween = create_tween()`
` tween.set_trans(Tween.TRANS_QUAD)`
` tween.set_ease(Tween.EASE_OUT)`
` var jump_height := 20.0 #randf_range(20.0, 60.0)`
` tween.tween_property(sprite_2d, "position:y", land_position.y - jump_height, HALF_FLIGHT_TIME)`
` tween.tween_property(shadow, "position:y", land_position.y + jump_height, HALF_FLIGHT_TIME)`
` tween.set_ease(Tween.EASE_IN)`
` tween.tween_property(sprite_2d, "position:y", land_position.y, HALF_FLIGHT_TIME)`
`func _on_timer_timeout() -> void:`
` hopping()`
sorry for the codes Format. But the "Insert code block" of the options don't let my do it right. I don't kwon if is a Bug in the page10Oct. 08, 2024
func _input_eventdatiswousWhy wouldn't this work?
```gdscript
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
if event == InputEventMouseButton and event.is_pressed:
flame.visible = not flame.visible
```10Oct. 08, 2024
wait time solution vs my solutionSootyHi, This was my solution to the challenge, after looking at the answer though I presume I have done this in an un-optimal way, specifically the way the timer works. From what I could gather reading the documentation, if you put a value in the timer.start() that becomes the wait time. So I guess, my solution uses less code but what is the downside I may be unaware of?
```gdscript
extends Node2D
@onready var bird_sprite: Sprite2D = $BirdSprite
@onready var timer: Timer = $Timer
@onready var shadow: Sprite2D = $Shadow
func _ready() -> void:
timer.timeout.connect(_on_timer_timeout)
func _bird_hop() -> void:
#generate a random angle in radians
var random_angle := randf_range(0.0,2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle) #vector starts pointing to the right and rotates based on random angle
var random_distance := randf_range(0.0,30.0)
var landing_position := random_direction * random_distance
print(landing_position)
const FLIGHT_TIME := 0.250
const HALF_FLIGHT_TIME := FLIGHT_TIME/2
# TWEEN THE BIRD
var tween := create_tween()
tween.set_parallel()
#horizontal movement
tween.tween_property(bird_sprite,"position:x", landing_position.x , FLIGHT_TIME)
tween.tween_property(shadow,"position:x", landing_position.x, FLIGHT_TIME)
tween.tween_property(shadow,"position:y", landing_position.y + 19.0, FLIGHT_TIME)
#flip the sprite
if landing_position.x > 0.0:
bird_sprite.flip_h = true
elif landing_position.x < 0.0:
bird_sprite.flip_h = false
#vertical movement
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
var jump_height := randf_range(5.0,25.0)
#upwards half
tween.tween_property(bird_sprite,"position:y", landing_position.y - jump_height, HALF_FLIGHT_TIME)
print("ive jumped up")
#downwards half
tween.set_ease(Tween.EASE_IN)
tween.tween_property(bird_sprite, "position:y", landing_position.y, HALF_FLIGHT_TIME)
timer.start(randf_range(0.0,3.0))
func _on_timer_timeout ()-> void:
_bird_hop()
```
10Oct. 04, 2024
Queue free with anticipationsilver-foxHi,
I've try to adding anticipation to the animation, before the Items goes, but the animation doesn't look that good. look like jagged. I think it's the way I used the tweens, could it be? here is my code:
```gdscript
if event_is_mouse_click:
print("Clicked")
var sprite_2d := get_node("Sprite2D")
sprite_2d.scale = Vector2 (1.0, 1.0)
var tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(sprite_2d, "scale", Vector2 (1.8,1.8), 0.1)
tween.set_trans(Tween.TRANS_QUAD) tween.set_ease(Tween.EASE_OUT)
tween.tween_property(sprite_2d, "scale", Vector2 (0.0, 0.0), 0.08)
tween.finished.connect(queue_free)
```10Oct. 03, 2024
Got stuck on Bird challenge, do I start over?enraged-manateeHi,
I was able to complete the first challenge without peeking. But the bird challenge had me stumped. This is my first run-through of the course. You mention it may take a second run through.
Do you recommend I restart the course at this stage or complete it in its entirety first?20Sep. 30, 2024
My solution to the UI challenge using Global scriptPJHello! This is my solution to the UI challenge. I utilized the Messenger/Global script but approached it slightly differently from other solutions I’ve seen here. I'd love to hear any feedback or ideas for improvement!
First, I created an autoload script called `Global.gd`. Then, I modified the chest script and added a line that connects the `tree_exited` signal from the loot item to a function within the Global script. I also bound the loot item as an argument for that function:
```gdscript
loot_item.connect("tree_exited", Global._on_global_collected.bind(loot_item))
```
In the Global script, I set up variables to track the item counts. I also created a custom `collected` signal, which I connected to the `update_ui` function in the script attached to the main scene. Every time an item is collected, the Global script emits this signal.
Here’s the `Global.gd` script:
```gdscript
extends Node
signal collected
var apple_qty: int = 0
var potion_qty: int = 0
var key_qty: int = 0
func _on_global_collected(emitter) -> void:
if emitter.is_in_group("apple"):
apple_qty += 1
elif emitter.is_in_group("potion"):
potion_qty += 1
elif emitter.is_in_group("key"):
key_qty += 1
collected.emit()
```
And here is the mini_dungeon.gd script, that updates the UI:
```gdscript
extends Node2D
@onready var apple_counter: RichTextLabel = $UI/AppleCounter
@onready var potion_counter: RichTextLabel = $UI/PotionCounter
@onready var key_counter: RichTextLabel = $UI/KeyCounter
func _ready() -> void:
Global.collected.connect(_on_global_connected)
_update_ui()
func _update_ui() -> void:
apple_counter.text = "x" + str(Global.apple_qty)
potion_counter.text = "x" + str(Global.potion_qty)
key_counter.text = "x" + str(Global.key_qty)
func _on_global_connected() -> void:
_update_ui()
```
50Sep. 28, 2024
Difference between my torch code and yoursironclad-eelSo I tried doing the torch assigment. I defined the is_mouse_released variable in the _input_event and then did the if is_mouse_released
Inside the function I wrote toggle_flame() and a line later I defined it as $Flame.visible = not $Flame.visible
When I click the torch it instantly reignites itself. I looked at the solved torch script and noticed that you do the $Flame.visible = not ... stuff inside the conditional itself.
Why does that make a difference?
And I figured it would be better to use a function in case I might ever consider putting out torches in other ways than clicking it (like throwing waterballons at it etc)
30Sep. 27, 2024
Bird bonus challenge - Different jump position between shadow and birdGoatzHello !
I took the bonus challenge and wanted to make the bird jump and make the shadow follow it.
I'm not sure I had the best reasoning here code wise, but I managed to make it work so I'm pretty happy about it :' )
This being said, I had to make a workaround for the shadow tween to work and I'm not sure why. I'd love you guys' insight on this if you have time to take a look !
So first of all, in the bird scene, I added the shadow.tscn that I left at (0, 0) and I move the bird Sprite2D at (5, -18) so that it'd look good. The bird is on top of the shadow, and both are children of the root Node2D of the scene.
My code works fine (I think) for the bird jump, but I could notice a gap between the shadow and the bird. When printing the position of both the bird and the shadow, I noticed the gap was equal to those (5, -18) that I put to the bird's Sprite2D tranforms.
° In my code, I created a specific shadow_land_position variable that is equal to the land_position (for the bird) minus a Vector2(5.0, -18.0) to compensate for the bird position... It's the workaround I found to fix my problem, but I'm really not sure why I had to do this. Help ! :D
° I also wanted to take a little step further and tried to flip the sprite depending on the direction of the jump. It kinda works but not everytime and I'm not sure why either. Help² ! :D
(Sorry if it's not so clear, I had trouble putting this into words)
Here below, you'll find my code, thank you ! :
(I did this all on my own without looking at the solution, so I'm pretty sure my code is the messiest ever. I'm a beginner in programming and it's the first time I take a Godot course, so it was a big challenge)30Sep. 26, 2024
How does tween change values?RedAiHiSo I decided to challenge myself with the bird challenge and my initial thought was to do a tween animation with the following code.
`var tween = create_tween().set_ease(Tween.EASE_IN_OUT)`
`tween.tween_property(sprite_2d, "position", sprite_2d.position - Vector2(0, 20), time_for_animation / 2.0)`
`tween.tween_property(sprite_2d, "position", sprite_2d.position + Vector2(0, 20), time_for_animation / 2.0)`
My thought being - "First I will move the sprite up by 20px and then, once it moved to 20px up, I will simply move it down 20px from its current position, which at that time should be 20px above where it started".
However, once I tested this, I found my bird sprite going lower and lower with each timer timeout. But strangely enough, it didn't go lower if I simply made it loop infinitely and call it only once from the `_ready` function. The tween function then looking like so:
`var tween = create_tween().set_ease(Tween.EASE_IN_OUT).set_loops()`
` tween.tween_property(sprite_2d, "position", sprite_2d.position - Vector2(0, 20), time_for_animation / 2.0)`
` tween.tween_property(sprite_2d, "position", sprite_2d.position + Vector2(0, 20), time_for_animation / 2.0)`
After testing it out with `print()` functions to see what the actual `sprite_2d.position` value is before and after each tween call, I was surprised to see that in every stage, the position value was (0,0) when doing an infinite loop (without timer timeout signal, simply setting `.set_loops()` for the tween object) and it doesn't actually change with every animation iteration.
However, after removing the `.set_loops()` for tween and switching to calling it with every timeout signal, it appears that the tween only changes the actual values of `sprite_2d.position` **after** it finishes the full animation call? The final code for the tween animation looked like this:
`func play_hop_animation():`
`print("hop")`
`var timer_wait_time : float = timer.wait_time`
`var wait_time_between_animations : float = timer_wait_time * 0.8`
`var time_for_animation : float = timer_wait_time - wait_time_between_animations`
`var tween = create_tween().set_ease(Tween.EASE_IN_OUT)`
`print("before first tween: " + str(sprite_2d.position))`
`tween.tween_property(sprite_2d, "position", sprite_2d.position - Vector2(0, 20), time_for_animation / 2.0)`
`print("before second tween: " + str(sprite_2d.position))`
`tween.tween_property(sprite_2d, "position", sprite_2d.position + Vector2(0, 20), time_for_animation / 2.0)`
`print("after second tween: " + str(sprite_2d.position))`
And the output I got looked like this:
`hop`
`before first tween: (0, 0)`
`before second tween: (0, 0)`
`after second tween: (0, 0)`
`hop`
`before first tween: (0, 20)`
`before second tween: (0, 20)`
`after second tween: (0, 20)`
`hop`
`before first tween: (0, 40)`
`before second tween: (0, 40)`
`after second tween: (0, 40)`
I managed to fix this at the end by simply not adding the Vector2 value that I used to lift up the bird in the first tween and simply set the value to `sprite_2d.position`
But I'm still a bit confused why after the code executed the first animation, the value of the sprites position parameter didn't change before the second animation played and only the second one changed its value with this variant of the code.20Sep. 20, 2024
Torch: Variable defined under input function is removed? VerryHello! I love the solution with using `var = not var`, though I'd like to know what happens under the hood, so I defined `if` statement, where I change `torch_active` state. My question is:
It seems that the flame doesn't turn back on when the var `torch_active` is defined under the `if` statement (commented line 20), in the `_input_event()`. It has to be defined outside all functions, of course at the beginning of the script.
I have an explanation for it, but I'd like to know if I'm correct, please. My understanding is that when any variable is defined under `_input_event()` (or other event function), it is defined when mouse clicks (in this example), but after that happens, it's removed/forgotten? So it cannot get changed, checked again, etc.
Thank you!
```gdscript
extends Area2D
@onready var flame: Sprite2D = $Flame
var torch_active : bool = true
func _ready() -> void:
# This parameter of the shader material gives each flame a slightly different look and randomized animation.
flame.material.set("shader_parameter/offset", global_position * 0.1)
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
event = event as InputEventMouseButton
var torch_clicked : bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_released()
)
if torch_clicked:
#var torch_active : bool = true
if torch_active:
flame.visible = false
torch_active = false
else:
flame.visible = true
torch_active = true
```10Sep. 18, 2024
Navigating to scripts not in the Top-Left hierarchy enraged-manateeHi,
Today, I was trying to complete the resize challenge above without looking at any hints. I knew, I'd have to edit the item script. But my problem was, no matter how I drilled down in the DungeonGame scene, I could not find the item.gd anywhere.
Did we create the item.gd script in a prior lesson? I don't remember doing that, and couldn't find it. Figured I'd have to create one.
But then I got stuck figuring out how to reference the items packed scenes in a new script. I finally gave up and peeked at the hints above to see that items.gd already existed.
Anyway, I know if I have orphaned scripts laying around, I'll lose them. is there a way to find Items.gd by drilling down from the MiniDungeon scene? Hope I'm making sense.. Thanks
10Sep. 17, 2024
Prevent items overlapping challengePaulI was interested in the problem of stopping the items overlapping, so decided to take this on as a challenge.
I've introduced some new code into the spawn function to try to get this to work, but after some time of fiddling it's not working so if there's any guidance on what I could look into that would be superb. The function is below.
My hope was to loop from the position, and an area around the position. If a match was found on the x & y axis, it would generate a new land_position until it was clear by the given amount (100 in the code below). In this current form, this would only work for 2 items, so I suppose I would need to store each instance's position in an array, and loop around those or something as well.
I'm sure there's a much better way of achieving this! It was good practice writing loops and such though so, hopefully wasn't wasted time even if it is a load of rubbish.
```gdscript
func _spawn_random_loot() -> void:
var last_land_position := Vector2(0.0, 0.0)
var land_position := Vector2(0.0, 0.0)
var same_x_area := true
var same_y_area := true
var loot_item: Area2D = possible_items.pick_random().instantiate()
add_child(loot_item)
while same_x_area and same_y_area:
var random_angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle)
var random_distance := randf_range(60.0, 100.0)
land_position = random_direction * random_distance
for last_land_x in range(last_land_position.x - 100, last_land_position.x + 100):
if land_position.x == last_land_x:
same_x_area = true
break
else:
same_x_area = false
for last_land_y in range(last_land_position.y - 100, last_land_position.y + 100):
if land_position.y == last_land_y:
same_y_area = true
break
else:
same_y_area = false
last_land_position = land_position
same_x_area = true
same_y_area = true
const FLIGHT_TIME := 0.4
const HALF_FLIGHT_TIME := FLIGHT_TIME / 2
var tween := create_tween()
tween.set_parallel()
loot_item.scale = Vector2(0.25, 0.25)
tween.tween_property(loot_item, "scale", Vector2(1.0, 1.0), HALF_FLIGHT_TIME)
tween.tween_property(loot_item, "position:x", land_position.x, FLIGHT_TIME)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
var jump_height := randf_range(80.0, 150.0)
tween.tween_property(loot_item, "position:y", land_position.y - jump_height, HALF_FLIGHT_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(loot_item, "position:y", land_position.y, HALF_FLIGHT_TIME)
```80Sep. 17, 2024
Hoppingrewarding-chimpanzeeI seem to have a problem with the bird hopping, when I run the main scene (there are 3 birds in it ) they all seem to jump at the start of the scene all at once and then at the set random intervals my Timer node is setup in the inspector maybe it gets the values at wrong orders or smth. My script for the bird :
```gdscript
extends Node2D
@onready var sprite_2d: Sprite2D = $Sprite2D
@onready var timer: Timer = $Timer
func _ready() -> void:
timer.wait_time = randf_range(2.0, 8.0)
func _on_timer_timeout() -> void:
hop()
func hop() -> void:
const HOP_TIME := 0.35
const HALF_HOP_TIME := HOP_TIME / 2.0
var random_angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle)
var random_distance := randf_range(10.0, 35.0)
var land_position := random_direction * random_distance
var jump_hight := randf_range(15.0, 25.0)
var tween = create_tween()
tween.set_parallel(true)
tween.tween_property(sprite_2d, "position:x", land_position.x, HOP_TIME)
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_QUAD)
tween.tween_property(sprite_2d, "position:y", land_position.y - jump_hight, HALF_HOP_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(sprite_2d, "position:y", land_position.y , HALF_HOP_TIME)
if sprite_2d.position.x >= land_position.x:
sprite_2d.flip_h = false
else :
sprite_2d.flip_h = true
```10Sep. 17, 2024
A few question after completing the bird hopping challengeVertPingouinAfter making my birds hop, I got the hop of the two birds I added synchronized with a repeating timer. After looking up the complete code, I'm puzzled about the fact that the birds seems both to have their own timer with different wait_time. But there is only one timer, how can this be ?
Another thing I don't quite get : after letting my birds hop freely for a long time, I expected them to go away randomly offscreen but they seem stuck to their start position (which is good indeed but I can figure out why).40Sep. 13, 2024
Note about the phrase "divide and conquer"raincloudThe phrase is commonly used within Western countries to refer to the process of breaking a project into smaller tasks, and in some cases allocating those tasks between members of a team. However, the phrase originates with brutal colonialist tactics that involve exploiting divisions within a colonized people in order to subjugate them. Folks taking this course who live in formerly (or currently) colonized territories may find the original meaning more apparent, and painful, than the meaning you're intending. I would encourage you to choose a different phrase.10Sep. 07, 2024
Lighting the torch againServin```gdscript
func _input_event(Viewport: Node, event: InputEvent, shape_index: int) -> void:
var event_is_mouse_click: bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if event_is_mouse_click:
flame_invisible()
var event_is_right_click: bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_RIGHT and
event.is_pressed()
)
if event_is_right_click:
flame_visible()
func flame_invisible() -> void:
var tween = create_tween()
tween.tween_property(flame, "scale", Vector2(), 3)
tween.finished.connect(flame.queue_free)
func flame_visible() -> void:
var tween = create_tween()
tween.tween_property(flame, "scale", Vector2(0.2,0.2), 1)
```30Sep. 07, 2024
Set tweens for torch lighting and unlightingunripe-tarsierI don't know if this is good but I got it working like this on the torch.
extends Area2D
@onready var flame: Sprite2D = $Flame
@onready var torch: Area2D = self
var point = Vector2(0,0)
func _ready() -> void:
var shape_index = torch.get_index()
# This parameter of the shader material gives each flame a slightly different look and randomized animation.
flame.material.set("shader_parameter/offset", global_position * 0.1)
#flame.set_visible(false)
torch.input_event.connect( _on_input_event)
func _on_input_event(viewport: , event: InputEvent , shape_index):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
if flame.is_visible() == true:
var off_tween = create_tween()
off_tween.tween_property(flame , "scale", Vector2(0,0), 1)
await off_tween.finished
flame.set_visible(false)
elif flame.is_visible() == false:
var on_tween = create_tween()
on_tween.tween_property(flame , "scale" , Vector2(.25,.25) , 0.5)
flame.set_visible(true)
30Sep. 05, 2024
Completed the bird challenge! (Though my code is a bit different to the teacher’s)FioretinHere’s the code that I wrote for the hopping bird challenge. It’s different than the one here though, is that okay?
```gdscript
extends Node2D
@onready var timer := $Timer
@onready var sparrow := $Sparrow
@onready var shadow := $Shadow
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
_on_timer_timeout()
func hopping_animation() -> void:
var random_angle := randf_range(0.0, 2 * PI)
var random_direction := Vector2.RIGHT.rotated(random_angle)
var random_distance := randf_range(20.0, 40.0)
var random_position := random_direction * random_distance
const FLIGHT_TIME := 0.4
const HALF_FLIGHT_TIME := FLIGHT_TIME / 2.0
var tween := sparrow.create_tween()
tween.set_parallel()
#Shadow's tween:
tween.tween_property(shadow, "position:y", random_position.y + 20, FLIGHT_TIME)
tween.tween_property(shadow, "position:x", random_position.x - 6, FLIGHT_TIME)
#Bird's tween:
tween.tween_property(sparrow, "position:x", random_position.x, FLIGHT_TIME)
tween.tween_property(sparrow,"position:y", random_position.y - 40.0, HALF_FLIGHT_TIME)
tween.tween_property(sparrow,"position:y", random_position.y, HALF_FLIGHT_TIME)
func _on_timer_timeout() -> void:
hopping_animation()
```
When I did this, I noticed that the shadow isn’t placed neatly beneath the bird when it hops, so I added +20 to the `random_position.y` and -6 to `random_position.x` for the shadow’s tween properties10Sep. 03, 2024
Excellent lesson and challengesLaevusThanks for this one. The chance to play around like this within the confines of the course and immediately after learning about the documentation was a fantastic chance to see what I could remember from earlier lessons.
As soon as I saw the bird hop challenge, I knew I was going to do it (my partner and me both love birds), but I had to do an important thing before making it hop – make it sparkle for three seconds if you click on it and show a little message to say you've just fed it. When showing my partner, and without telling her, she saw the bird hop and immediately tried to click on it to feed it :D Worth it!
It was good practice to make sure the hierarchies were set up so the sprite, shadow, and collision area all moved with each hop. This is the first time in the course that multiple things have clicked and I feel like I can do some ideas that come to mind, and I now can't wait to do more of the course.10Sep. 02, 2024
my shadow desyncs on first jump cautious-crabwhen the bird jump for some reason the shadow consistently desyncs from it. no idea on why it happens, even checked reference and should be the same to me but still the shadow is not synchronized to the bird's landing spot
```gdscript
extends Node2D
@onready var timer := $JumpTimer
@onready var bird := $BirdSprite
@onready var shadow := $Shadow
func _ready() -> void:
timer.timeout.connect(jump)
func jump() -> void:
print("pulei")
var random_angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle)
var random_distance := randf_range(0, 30.0)
var jump_position := random_direction * random_distance
var jump_height := randf_range(10.0, 20.0)
const JUMP_TIME := 0.25
var tween := create_tween()
tween.set_parallel()
tween.tween_property(bird, "position:x", jump_position.x, JUMP_TIME)
tween.tween_property(shadow, "position", jump_position, JUMP_TIME)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(bird, "position:y", jump_position.y - jump_height, JUMP_TIME/2)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(bird, "position:y", jump_position.y, JUMP_TIME/2)
```40Sep. 02, 2024
Two items overlappingHarbingerShI saw some people saying that with `get_viewport().set_input_as_handled()` they are collecting the bottom-most item, I have no idea what they are doing wrong, however, it's working fine for me,
this is the code attached to `item.gd` script on the `_input_event()` function:
```gdscript
@export_category("items Types")
enum ITEMS {
APPLE,
KEY,
POTION
}
@export var type: ITEMS
func _on_collect_item() -> void:
input_pickable = !input_pickable
match type:
ITEMS.APPLE:
get_tree().current_scene.apples += 1
ITEMS.KEY:
get_tree().current_scene.keys += 1
ITEMS.POTION:
get_tree().current_scene.potions += 1
var tween: Tween = create_tween()
tween.set_ease(Tween.EASE_IN_OUT)
tween.tween_property(self, "scale", Vector2(1.2, 1.2), 0.1)
tween.tween_property(self, "scale", Vector2(0.0, 0.0), 0.1)
await tween.finished
queue_free()
func _input_event(_viewport: Viewport, event: InputEvent, _shape_idx: int) -> void:
if event is InputEventMouseButton && event.button_index == MOUSE_BUTTON_LEFT && event.pressed:
get_viewport().set_input_as_handled()
_on_collect_item()
```
here i created a new script attatched to the MiniDungeon node (the root node) so i can group all items in one base node:
```gdscript
signal spawn_object(object, new_position: Vector2)
@onready var objects: Node2D = %Objects
var apples: int = 0:
set(amount):
apples = amount
_on_update_texts("UI/Container/Apple/Amount", str(amount))
var keys: int = 0:
set(amount):
keys = amount
_on_update_texts("UI/Container/Key/Amount", str(amount))
var potions: int = 0:
set(amount):
potions = amount
_on_update_texts("UI/Container/Potion/Amount", str(amount))
func _ready() -> void:
spawn_object.connect(_on_spawn_object)
func _on_spawn_object(object, new_position: Vector2) -> void:
objects.add_child(object)
object.global_position = new_position
func _on_update_texts(node_path: String, amount: String) -> void:
get_node(node_path).set_text("x" + amount)
```
here i added a new node to the scene tree a `Node2D` for Objects only as a child of the MiniDungeon node
and this is on the `chest.gd` script where we spawn random items around the chest:
```gdscript
func _on_spawn_items() -> void:
var new_item = items.pick_random().instantiate()
#we add items to the object node which hold all items in the entire GAEM!
#we pass the global position of the current scene so it spawn relevant to this scene position
get_tree().current_scene.spawn_object.emit(new_item, global_position)
var random_angle: float = randf_range(0.0, 2.0 * PI)
var new_direction: Vector2 = Vector2.RIGHT.rotated(random_angle)
var new_distence: float = randf_range(60.0, 120.0)
var target_position: Vector2 = new_direction * new_distence
#we added the global position from the target position so it will be relevant //
#to the global position of the chest and so the item lands in the correct target position
new_item._on_item_droped(target_position + global_position)
#we do that so the items always be on top of all chests plus organize all items in one node
func _on_open() -> void:
input_pickable = !input_pickable
animation.play("Open")
if !items.is_empty():
for item in range(randi_range(1, 3)):
_on_spawn_items()
func _input_event(_viewport: Viewport, event: InputEvent, _shape_idx: int) -> void:
if event is InputEventMouseButton && event.button_index == MOUSE_BUTTON_LEFT && event.pressed:
_on_open()
```
in this case I am alomst always collecting the top-most item
i am using Godot 4.2.210Sep. 01, 2024
Change Shadow Scale to Show Change in Distance From GroundiguessfiveA good extra feature for the bird challenge is to change the shadow's scale property when leaving the ground and restoring it back when landing. To make the bird look like it's leaving the ground when jumping.
My solution for this is:
```gdscript
# Stored the original scale of the shadow in constant
const ORIGINAL_SHADOW_SIZE := Vector2(1.563, 0.977)
# Created a constant for the scale factor to change the shadow by
const SHADOW_SCALE_FACTOR = 0.9
# Created a new tween to run in parallel with other tweens
tween = create_tween()
tween.tween_property(shadow, "scale", shadow.scale * SHADOW_SCALE_FACTOR, HALF_THE_TIME_FOR_EACH_HOP)
tween.tween_property(shadow, "scale", ORIGINAL_SHADOW_SIZE, HALF_THE_TIME_FOR_EACH_HOP)
```
There is a slight bug when the bird jumps south of the screen, the shadow looks out of place. Any tips to fix this?30Aug. 25, 2024
my solution to the collecting challenge with a message busmrelectroni tried to use a message bus for this challange, so here is what i did:
created a message bus like so:
```gdscript
extends Node
signal item_update_should_trigger(item_name:String)
```
then set it to autoload from the project settings by going to project>project settings>autoload tab:
selected the path to the script, then added it.
after that, in the item.gd which is the script attached to all three items, i have added comments prefixed with mychanges to describe what i did and my thought process:
Script for pickable items. We don't use inheritance here, just composition: an item is any Area2D scene with a child sprite and this script attached. See gem.tscn and health_pack.tscn.
```gdscript
extends Area2D
#signal item_update_should_trigger(item_name:String)
@onready var sprite_2d :Sprite2D = get_node("Sprite2D")
#mychanges: added to store the item name when its first instantiated
var item_name : String =""
#mychanges: added to store the deuration of the distruction tween
var run_duration: float=0.5
func _ready() -> void:
play_floating_animation()
#mychanges: added to connect the input event to my function
input_event.connect(_on_mouse_pressed)
func play_floating_animation() -> void:
var tween := create_tween()
tween.set_loops()
tween.set_trans(Tween.TRANS_SINE)
var position_offset := Vector2(0.0, 4.0)
var duration = randf_range(0.8, 1.2)
sprite_2d.position = -1.0 * position_offset
tween.tween_property(sprite_2d, "position", position_offset, duration)
tween.tween_property(sprite_2d, "position", -1.0 * position_offset, duration)
#mychanges: the function that will be called when the input_event signal is emitted
func _on_mouse_pressed(viewport: Node, event:InputEvent, shape_idx: int)->void:
#mychanges:logic to detect that the event is a left mouse click
var event_is_mouse_click: bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if event_is_mouse_click:
#mychanges: emiting to the message bus that an item update should
#trigger with the item name as an arg
MessageBus.item_update_should_trigger.emit(item_name)
#mychanges: a simple tween animation, since just disapearing is boring
var tween = create_tween()
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(sprite_2d,"scale",Vector2(0,0),run_duration)
#mychanges: connecting to the tween finished signal so that the queue_free
#isn't called before the tween finishes
tween.finished.connect(_queue_free)
#mychanges: just calles the queue_free, used after the tween finishes
func _queue_free()->void:
queue_free()
```
then i made some changes to the spawning function in the chest.gd
```gdscript
func _spawn_random_item() ->void:
var loot_item_scene :PackedScene= possible_items.pick_random()
var loot_item :Area2D=loot_item_scene.instantiate()
add_child(loot_item)
var idx:=possible_items.find(loot_item_scene)
if idx==0:
loot_item.item_name="apple"
elif idx==1:
loot_item.item_name="key"
elif idx==2:
loot_item.item_name="potion"
#....
```
basically, i just added a way to detect what item is going to be spawned and set its name property, to be honest, i don't like this approach, its too tightly coupled, and if i added a fourth item, then i will have to update this script. but that was the best i could come up with atm
after that, i created the UI as a new scene, it basicaly looks like this:
[https://postimg.cc/8fHf3Gr2](https://postimg.cc/8fHf3Gr2)
and here is the ui.dg script attached to the control node.
```gdscript
extends Control
#getting refrences to the label nodes for future updating
@onready var label_potion: Label = $LabelPotion
@onready var label_key: Label = $LabelKey
@onready var label_apple: Label = $LabelApple
#initial data
var potion_count:=0
var key_count:=0
var apple_count:=0
func _ready() -> void:
#connecting the update_item function with the item_update_should_trigger
#signal from the message bus
MessageBus.connect("item_update_should_trigger",_update_item)
#setting inital text
label_apple.text="x"+str(apple_count)
label_key.text="x"+str(key_count)
label_potion.text="x"+str(potion_count)
#the update item function
func _update_item(item_name: String)-> void:
#logic to increment the state based on the item name received by the signal
if item_name=="apple":
apple_count+=1
elif item_name=="key":
key_count+=1
elif item_name=="potion":
potion_count+=1
#updating the ui
label_apple.text="x"+str(apple_count)
label_key.text="x"+str(key_count)
label_potion.text="x"+str(potion_count)
```
i used this video to learn about the message bus
[https://www.youtube.com/watch?v=vbw1ncvSUYg](https://www.youtube.com/watch?v=vbw1ncvSUYg)
would love any and all feedback on how to improve further, and if my solution has any glaring inefficiency or bad practices
10Aug. 24, 2024
Question - why can't I use timer node's own timeout signal to restart itself?MatejHello again! So in the very last code reference (bird_completed.gd ), in the last line (line 43), we use this to restart the timer:
```gdscript
tween.finished.connect(wait_timer.start)
```
And I understand completely why in this instance, we wait until the last piece of animation finishes until we restart the timer. This prevents the bird hopping animation from restarting too soon (if the 'wait_time' timer property is short enough and tween animation duration long enough that is).
That being said, what I tried to do in place of that line while trying to solve the challenge was:
```gdscript
wait_timer.timeout.connect(wait_timer.start)
# 👆 Well that didn't work!
# So I replaced that with the following line.
wait_timer.start()
```
Ignoring that this might lead to unwarned behavior involving tween animations, it brought up a question - why the 'wait_timer.timeout.connect(wait_timer.start)' line doesn't restart the one shot timer?20Aug. 14, 2024
My answer for the UI challengeuntrue-giraffeUsing what I know atm this is my solution for the UI challenge
I would love to know if doing this is incorrect in any way.
I don't know if getting a node reference from far away on the tree is slow or something.
Sorry for the bad formatting, the code block is not working on my end.
First I created the UI elements and added this script to the parent control node:
#collected.gd
extends Control
var apple_count = 0
var key_count= 0
var potion_count = 0
@onready var label_apple: Label = $VBoxContainer/HBoxContainer/Label_apple
@onready var label_key: Label = $VBoxContainer/HBoxContainer2/Label_key
@onready var label_potion: Label = $VBoxContainer/HBoxContainer3/Label_Potion
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
label_apple.text = "0"
label_key.text = "0"
label_potion.text = "0"
func add_point(item_name)-> void:
print(item_name)
match item_name:
"apple":
apple_count+=1
label_apple.text = "x" + str(apple_count)
"key":
key_count+=1
label_key.text = "x" + str(key_count)
"potion":
potion_count+=1
label_potion.text = "x" + str(potion_count)
Then on every item scene I assigned them a group and in its script, I checked to which group they belong, on left mouse press:
#item.gd
signal Collected(item_name)
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
...
var item_type:= get_item_type()
Collected.emit(item_type)
func get_item_type()-> String:
if is_in_group("apple"):
return "apple"
elif is_in_group("key"):
return "key"
else:
return "potion"
and lastly on the chest after creating the scene instance I connected the ui with the signal from the item element:
var item_instance: Area2D = possible_items.pick_random().instantiate()
add_child(item_instance)
var collected_ui = $"../CanvasLayer/Collected"
if collected_ui:
item_instance.connect("Collected", Callable(collected_ui, "add_point"))10Aug. 14, 2024
Bird Challenge - Anticipation before jump animationIronEvaI have struggled with adding a little flavour to the Bird Challenge.
What I want to happen is this:
**1)** Do an anticipation before the jump which I will fake by using the Skew on the transform.
**2)** Then go back to the original skew value, faking a push-off on the jump.
**1** and **2** I know I want these to play in a set order, which is perfect as by default tweens work this way. It gets tricky when I create another tween as it then wants to play in parallel with the first part of the animation, which I don't want to happen.
Is there a way to delay the creation of a tween? I tried to use the tween.interval but I just got errors for that?
I must have spent about 5 hours trying to figure out what I have done wrong and feel like I am losing hair. ;)
```gdscript
func _on_timeout():
var random_angle = randf_range(0.0, 2.0) * PI
var random_direction = Vector2(1.0, 0).rotated(random_angle)
var random_jump_distance = randf_range(25.0, 75.0)
var landing_position = random_direction * random_jump_distance
#print(random_jump_distance, random_direction, landing_position)
var jump_height: float = randf_range(30.0, 60.0)
var time_distance: float = 0.5
var half_time_distance: float = time_distance / 2
#---------------------Anticipation----------------
#create a tween to do the following, do an anticipation then, go back to normal skew
var tween = create_tween().set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
#Do an anticipation before the jump
tween.tween_property(bird_sprite_2d, 'skew', 0.6, time_distance)
#learn forward and go back to normal skew value
tween.tween_property(bird_sprite_2d, 'skew', 0, 0.25)
#-------------------------------------------------
#--------------------Move to Location------------
#need a wait to delay the tween creation but tween.interval(1) didn't work
tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
tween.tween_property(self, 'position', position + landing_position, time_distance)
#------JUMP--------------------------------------
tween = create_tween()
tween.tween_property(bird_sprite_2d, 'position:y', bird_sprite_2d.position.y - jump_height, half_time_distance)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(bird_sprite_2d, 'position:y', bird_sprite_2d.position.y, time_distance)
```40Aug. 12, 2024
Flipping with @export and setter OctarinaI am so happy for this course, I feel I am starting to be able to do more what I want with the code now. I really appreciate these experiments, I feel I learn a lot and also from other's attempts! :D
What I wanted was to do the FLIP challenge, and also make it so one can in-editor set the direction on bird.
```gdscript
enum look {LEFT, RIGHT}
@export var direction :look:
set(value):
direction = value
if sprite_2d and sprite_2d.is_inside_tree():
if value == look.RIGHT:
sprite_2d.flip_h = true
elif value == look.LEFT:
sprite_2d.flip_h = false
get:
return direction
```
Then use it like this:
```gdscript
if land_position.x > 0: direction = look.LEFT
else: direction = look.RIGHT
```
Now I am sure I got this to work once, it updated in editor if I add @tool and also worked when running from editor. Now it feels sometimes the flip is off, shadow gone ect. Or is this doomed to cause issues?
I really like that I can see that direct feedback in-editor how something looks or behaves when I change a property.
I get a sense this is an "over complicated" solution I found, is this the right way to approach things? It feels a bit odd to check is inside tree. I found that searching since it crashed sometimes on start. I tried to really understand the order things run in, but it seems like the setter is called before _ready and @onready?40Jul. 31, 2024
Torches keep switching on/off in sync.GillooI am having an issue while doing the torch challenge where, despite using an Area2D and making sure Local To Scene is switched on, both torches switch on and off at the same time when clicked.
Does anyone else have the same issue?70Jul. 30, 2024
Answer to Torch challengelazy-barracudaMay be I am not the sharpest tool in the shed and I didn`t use any hints, so my code became a lil bit stupid.
BUT IT WORKS :D
```gdscript
extends Node2D
@onready var flame: Sprite2D = $Flame
@onready var check_flame := true
func _ready() -> void:
# This parameter of the shader material gives each flame a slightly different look and randomized animation.
flame.material.set("shader_parameter/offset", global_position * 0.1)
func _input_event(viewport: Viewport, event: InputEvent, shape_index: int):
var event_is_mouse_click: bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if event_is_mouse_click:
if check_flame == true:
remove_child(flame)
check_flame = false
else:
add_child(flame)
check_flame = true
```10Jul. 28, 2024
Tween execution orderploped00Hi all!
I solved bird's challenge in a very similar way than GDQuest (see below), but I'm confused about the instructions I gave and the order in which Godot executes the tweens. Here are my doubts:
- When I create a new tween using tween = create_tween() am I substituting the previous tween by the new one?
- Why do the 2 tweens (pos X and pos Y) executes in parallel if I'm not using the set_parallel() method?
- I've conclude that I can use the same tween to animate different properties but I've to reinitialize the tween when I animate different sub-properties as position:x and position:y, am I right?
Thank you very much in advance!
`var tween := create_tween()`
` tween.tween_property($Sprite2D, "position:x", random_movement.x, JUMP_TIME)`
` tween = create_tween()`
` tween.set_trans(Tween.TRANS_QUAD)`
` tween.tween_property($Sprite2D, "position:y", random_movement.y - jump_height, JUMP_TIME/2)`
` tween.tween_property($Sprite2D, "position:y", random_movement.y, JUMP_TIME/2)`
` shadow.position = random_movement + shadow_offset`20Jul. 22, 2024
Using AnimationPlayer and randomized Area2D Transform Parametersdelicious-frogI was trying to do this by creating a short hop animation using AnimationPlayer, but I was having the following issues:
1. Edit the transform parameters (rotation/orientation) of the animation, the parent Node2D, or the Sprite2D
I was thinking that I could either change the angle and randomize a horizontal mirror of the node to force the animation to play the hop animation in a random direction.
1. Change the (x, y) position of the key frames of the animation editor. I'm not sure if it is something that can or should be done, but I saw the AnimationTrackKeyEdit (x, y) values and thought that I could modify their values to match randomized distance, height, and angle.
2. I was having an issue with the bird spawing either at (0, 0) of the mini_dungeon or slightly behind it. The issue fixed itself when I changed how I did the animation. I think that the bird is inhereting its position from the mini_dungeon scene or the AnimationPlayer was forcing its coordinates to (0, 0), but I didn't fix the issue so I can't say for sure. 20Jul. 15, 2024
New defi because need trainingHazlarhi !
I had a hard time re-doing the challenge with the bird, and I would like to get revenge by giving myself another challenge which is that when I click on the bird, it stops its interpolation and I can move it, and that if I direct it towards the torch it gradually blackens and disappears (using an interpolation) or that if I release it it restarts its original interpolation.
Is this a challenge that can be met with the knowledge taught so far?100Jul. 15, 2024
Getting lost in coordinate spaces for the bird challengeprofuse-otter```gdscript
extends Area2D
@onready var bird_sprite: Sprite2D = $"Bird Sprite"
@onready var shadow: Sprite2D = $Shadow
@onready var timer: Timer = $Timer
func _ready() -> void:
timer.timeout.connect(hop)
func _process(delta: float) -> void:
pass
func hop() -> void:
var random_angle := randf_range(0, 2 * PI)
var random_direction := Vector2.from_angle(random_angle)
var random_distance := randf_range(15.0, 30.0)
var landing_position: Vector2 = random_direction * random_distance
const HOP_DURATION := 0.6
# flip the bird sprite if bird is not facing the direction
# adjust the position if it flips to move its feet back to the centre of the shadow
if (random_angle < PI / 2 or random_angle > (3.0 * PI) / 2.0):
if not bird_sprite.flip_h:
bird_sprite.position.x -= 14
bird_sprite.flip_h = true
elif bird_sprite.flip_h:
bird_sprite.position.x += 14
bird_sprite.flip_h = false
var tween := create_tween()
tween.tween_property(bird_sprite, "position:x", landing_position.x + bird_sprite.position.x, HOP_DURATION)
tween = create_tween()
tween.tween_property(bird_sprite, "position:y", bird_sprite.position.y - 20, HOP_DURATION / 2)
tween.tween_property(bird_sprite, "position:y", landing_position.y + bird_sprite.position.y, HOP_DURATION / 2)
tween = create_tween()
tween.tween_property(shadow, "position", landing_position + shadow.position, HOP_DURATION)
tween = create_tween()
const SHADOW_BASE_SCALE := Vector2(1.563, 0.977)
#remap(bird_sprite.position.y - 20, )
tween.tween_property(shadow, "scale", SHADOW_BASE_SCALE * 1.4, HOP_DURATION / 2)
tween.tween_property(shadow, "scale", SHADOW_BASE_SCALE, HOP_DURATION / 2)
tween = create_tween()
tween.tween_property(shadow, "modulate:a", 0.7, HOP_DURATION / 2)
tween.tween_property(shadow, "modulate:a", 1.0, HOP_DURATION / 2)
```
Overall I'm quite happy with the result of the above code. The bird hops in a random direction, looks in the direction it is hopping, and the shadow's position remains in sync with it.
I'm not so happy with the use of different (I think?) coordinate spaces in my solution. I think it started by trying to make the vertical jump animation look more consistent between when the landing position was above or below the origin (a byproduct of having to use the y-axis for both height and depth?) I'm not even sure how to phrase what I'm asking but it seems like there is a much more understandable solution that doesn't involve self-referencing positions in the tween?
Another area I know has a less rigid solution is where I flip the bird sprite and adjust the position. I'm guessing the answer lies in the scene editor but I can't work it out, any suggestions?
30Jul. 12, 2024
Input Events for key pressessteel_jokersAre the input events we are using only relevant to mouse input or can we capture an event that occurs when a user presses a key on their keyboard?
For instance, if I wanted to capture that a body entered the area where the chest was and the user pressed a key, let's say 'o' for open, would it work?
I looked up InputEventAction and thought I was maybe on the right track to achieve this, but I can't seem to get it working.
I've tried a few things, but I'm not finding success:
```gdscript
# Note: I've added an open action to my InputMap that corresponds to the 'o' key
func _input_event(viewport, event, shape_idx):
var keypress_event: bool = (
event is InputEventAction and
event.action == "open" and
event.is_action_pressed()
)
# to check if it works, I'm printing the result to the console
print("o key was pressed: ", keypress_event)
```20Jul. 07, 2024
var tween - why can we reuse this X times in a script? snowm8nI'm reading through my code, trying to get a quick refresh and make sure I understand everything so far (I'm just about to turn off the torch). I noticed we reuse "var tween" several times, but we usually need to come up with a unique variable name. Is there an exception because it's a tween? This may be a(nother) rookie question but I wanted to ask :-)
*ex. I would think we needed a unique variable name for each time its called in a single script:*
```gdscript
var tween_mouse_entered
var tween_mouse_exited
var tween_spawn_random_item
```10Jul. 05, 2024
Weird hopping movement, when the bird jumps downwardsLxVaiNI made a code for the hopping bird and im happy with it, for my personal skills, since its my first try on this, but one thing is weird. When the bird hops downwards, it does make a weird double wave motion instead of one continuous one. I couldnt figure out why. It's probably because the vectors add up in a weird way when the x coordinate is almost 0 and y > jump height.
Here is my code, if someone wants a "challenge" and figure it out for me xD (i made the shadow and the sparrow sprite in one canvas group so they jump together if you are wondering)
@onready var sprite = $CanvasGroup/Sparrow
@onready var sparrow = $CanvasGroup
func _ready() -> void:
$Timer.timeout.connect(_on_timeout)
func _on_timeout()->void:
var random_direction = Vector2(50.0,0.0).rotated(randf_range(0.0,2*PI))
if random_direction.x > 0:
sprite.flip_h = true
elif random_direction.x < 0:
sprite.flip_h = false
var tween = create_tween().set_parallel()
tween.tween_property(sparrow,"position:x",random_direction.x,0.4)
tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUAD)
tween.tween_property(sparrow,"position:y",random_direction.y - 20.0,0.2)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(sparrow,"position:y",random_direction.y,0.2)10Jun. 29, 2024
Bird and shadow are disaligned at the first jumpbururezHi, thanks for your great work!
I don't understand what is wrong in my code, in the first jump seems that shadows has different land position:
```gdscript
extends Node2D
@onready var birdSprite := $Sparrow
@onready var birdShadowSprite := $Shadow
@onready var timer := $Timer
func _ready() -> void:
timer.one_shot = true
timer.wait_time = randf_range(1.0, 3.0)
timer.timeout.connect(_on_timeout)
timer.start()
func _on_timeout() -> void:
var random_direction := Vector2(1.0, 0.0).rotated(randf() * 2.0 * PI)
var random_distance := randf_range(0.0, 30.0)
var new_position := random_direction * random_distance
_hop(new_position)
func _hop(final_destination: Vector2) -> void:
const hop_height := 16.0
const hop_time := 0.25
const half_hop_time := hop_time / 2.0
var tween := create_tween().set_parallel()
tween.tween_property(birdSprite, 'position:x', final_destination.x, hop_time)
tween.tween_property(birdShadowSprite, 'position', final_destination, hop_time)
tween = create_tween().set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(birdSprite, 'position:y', final_destination.y - hop_height, half_hop_time)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(birdSprite, 'position:y', final_destination.y, half_hop_time)
timer.wait_time = randf_range(1.0, 3.0)
tween.finished.connect(timer.start)
```
Could anyone help me to understand?
Thanks40Jun. 29, 2024
Where should I attach the script?Nikita MyshkinPlease tell me which node the item script needs to be attached item.gd for the task "step 1: Detecting mouse clicks". Or do I still need to write in the script chest.gd?
I tried to create separate scenes for items, as in the M5 module, but it doesn't work. `print("The condition is true")` is triggered only on new items, not on those in the chest. It seems to me that you need to create a new script and use the array, as in the script chest.gd. But I can't figure out where to attach it.50Jun. 27, 2024
How can I make it so the bird can't go through wallsFasionMagazineI want to make it so the birds can't go through walls or the chests. I was thinking I could the coordinates of where they can't go and check if the place they're going to land is in those coordinates and if so calculate a new landing place, but this seems convoluted. Is there a simpler solution?20Jun. 21, 2024
Am I Allowed To Look at Previous Projects?choodleCan I look at my other scripts to gain insights about how to solve this? I looked at chest.gd and item.gd and they helped a lot, however, if it would be better to do experiments like this without looking back please let me know.30Jun. 19, 2024
Torches on/off are synchronisedpuzzled-magpieHey, I couldn't find a solution to this. When I click one torch, both of them go on and off. The Local to Scene option in the shader was already on, so that wasn't it. Can someone give me a hint on this?50Jun. 14, 2024
A tip for anyone trying the UI item challengeInfiniteDazeI don't know if there's a more proper way to do it but you should look into how to create and pass signals from different scripts (especially instanced items). While it *is* technically similar to M5, I had to make the labels as part of the Main Dungeon scene, and it took a bit of document reading and googling to somehow stumble upon the right answer.
Was there an easier method to doing this?
Also the get_tree() is your best friend.30Jun. 13, 2024
Flame tweenp_dev_Hi there. I've written the following code to animate the flame so it shrinks before toggling it invisible, and grows when becoming visible again. While it works, I have a sense that something is off. How could I improve it? Thanks in advance.
```gdscript
# ...
if event_is_mouse_click:
var tween = create_tween()
if flame.visible:
tween.tween_property(flame, "scale", Vector2(0.0, 0.0), 0.2)
tween.finished.connect(change_flame_visibility)
else:
change_flame_visibility()
tween.tween_property(flame, "scale", Vector2(0.28038, 0.28038), 0.2)
func change_flame_visibility() -> void:
flame.visible = not flame.visible
```30Jun. 11, 2024
Bird double jumpProxerHi folks,
I am struggling with the following, I have my bird animation working and the shadow moving along with an offset and things initially look good, however, every couple of jumps (randomly) the bird seems to do a double jump? or the jump height doesn't seem to be consistent even I have set a constant for the height?
I have uploaded a video showing the issue here:
[https://file.io/vFi84B9r4GPY](https://file.io/vFi84B9r4GPY)
At seconds 04 and 10 is when the issue is clearly visible.
At the moment the animation gets executed every 01 second through a timeout signal.
Here's my scene script as well.
```gdscript
extends Node2D
@onready var bird_sprite: Sprite2D = $bird_sprite
@onready var shadow: Sprite2D = $Shadow
func _on_timer_timeout() -> void:
const ANIMATION_LENGHT := 0.25
const ANIMATION_HALF_LENGHT := ANIMATION_LENGHT / 2.0
var radious := 30.0
var angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(angle)
var random_distance := randf_range(20.0, radious)
var landing_position := random_direction * random_distance
var tween:= create_tween()
tween.set_parallel()
tween.tween_property(bird_sprite, "position:x", landing_position.x, ANIMATION_LENGHT)
var position_for_shadow = landing_position
position_for_shadow.y += 20
position_for_shadow.x -= 5
tween.tween_property(shadow, "position", position_for_shadow, ANIMATION_LENGHT)
tween = create_tween()
var height_jump := 16.0
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(bird_sprite, "position:y", landing_position.y - height_jump, ANIMATION_HALF_LENGHT)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(bird_sprite, "position:y", landing_position.y, ANIMATION_HALF_LENGHT)
```
Thanks in advance50Jun. 10, 2024
Where do I put this line of codeAphexget_viewport().set_physics_object_picking_sort(true)
get_viewport().set_physics_object_picking_first_only(true)
I want to know how to make items disappear one at a time when they're on top of each other but wherever I put the lines, my game crashes.
The error says Invalid call. Nonexistent function 'set_physics_object_picking_first_only' in base 'Window'.
I don't see the lines of code in the solution scripts unless I missed them. Thanks in advance!30Jun. 05, 2024
How is the event_is_mouse_clicked variable returning a true or false bool?leafI don't understand how the `event_is_mouse_clicked` variable is being returned as a bool. Exactly where is a true or false value being returned within this variable? Currently I am receiving a false value when I click a chest, but I don't even understand where in the syntax it would even be registering as true or false.
10May. 30, 2024
Question about an if/else statementA_Room_With_A_MooseThe code below produces an annoying bug where small movements along the x axis don't trigger my IF statement as expected:
```gdscript
# flips the sparrow's sprite horizontally
if random_direction.x > 0.0:
sparrow.flip_h = true
shadow.offset = Vector2(4.0, 18.0)
else:
sparrow.flip_h = false
```
My bird hops as expected for the most part, but with one caveat: small horizontal hops don't seem to trigger this statement, so if the bird takes small hops opposite the direction the sprite's currently facing (backward) it won't flip.
Any idea why this might occur?70May. 29, 2024
Using randf() vs randf_range() in bird_completed.gd scriptA_Room_With_A_MooseI was wondering how these two statements yield the same results:
```gdscript
(randf() * 2.0 * PI)
#vs
randf_range(0.0, 2.0 * PI)
```
According to the programmer's dictionary, randf()
> Returns a pseudo-random float between 0.0 and 1.0 (inclusive).
while randf_range()
> Returns a pseudo-random float between from and to (inclusive).50May. 29, 2024
Question about _input_event()MuseDoesI noticed a couple of things while doing these experiments. Specifically with the input event functions in the chest and item scripts.
In the chest.gd, the function is written like this:
func _input_event(viewport: Node, event: InputEvent, shape_index: int) -> void:
And in the item.gd it looks like this:
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
The arguments are different in both scripts. The viewport argument is declared as a Node and as a Viewport respectively.
And the shape_index changed to shape_idx when you use autocompletion.
I've tried switching the Viewport to Node and vice versa on both scripts. Nothing in particular seems to change. I used the Search Help, but I don't quite understand the entry:
----------------------------------------------------------------
void _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) virtual
Accepts unhandled InputEvents. shape_idx is the child index of the clicked Shape2D. Connect to input_event to easily pick up these events.
Note: _input_event() requires input_pickable to be true and at least one collision_layer bit to be set.
----------------------------------------------------------------
I guess what I'm trying to ask is:
Q1. What does changing the viewport argument from Viewport to Node really do?
and
Q2. Why does the shape_index return an int?
(If I understand this one, it seems like there's an integer associated with all of the children nodes within a given scene. But I couldn't find any more info in the Search Help, so I'm a little lost here.)
Q3. Why can the title for the argument for the shape_index be listed as both shape_index and shape_idx? Wouldn't changing the title of the argument give off an error normally?
20May. 26, 2024
Error when trying to tween the torch flames right-cheetahI wanted to use a tween to animate the flames being extinguished instead of just turning its visibility off. And I did manage to get it to work, but when I first extinguish the flames, and then make the items from the chest disappear by clicking on them, I come across this error:
Invalid get index 'queue_free' (on base: 'previously freed')
Can I get an explanation on what is happening here?
Here's my code for reference:
(torch.gd)
```gdscript
extends Area2D
@onready var flame: Sprite2D = $Flame
func _ready() -> void:
# This parameter of the shader material gives each flame a slightly different look and randomized animation.
flame.material.set("shader_parameter/offset", global_position * 0.1)
func _input_event(Viewport: Node, event: InputEvent, shape_index: int) -> void:
var event_is_mouse_click: bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if event_is_mouse_click:
disappear()
func disappear() -> void:
var tween = create_tween()
tween.tween_property(flame, "scale", Vector2(0.0,0.0), 0.05)
tween.finished.connect(flame.queue_free)
```10May. 25, 2024
Collecting the top itemEcepHey Nathan,
I've made it such that when you click on the collectible, it animates the collectible, adds to a player count for each type of collectible, and updates a UI element for each collectible type. However, one thing I'm struggling is making it so only the top-most collectible is collected in the case where they overlap. Right now I'm using:
```gdscript
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
if(event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT
and event.is_pressed()):
collect()
viewport.set_input_as_handled()
```
However, this makes it so the bottom-most collectible is collected and not the top. I'm not really sure where to go from here. I can't seem to find an online resource covering it.
Thanks in advance.70May. 24, 2024
Bird Challenge Tween the Node2D?opulent-boarHello, I enjoyed this section of the course and going through the challenges.
After completing the bird challenge, I was thinking about why we tween the shadow instead of the root Node2D for the bird? That is what I initially tried, it just makes more sense in my head to move the transform of the root node... but I guess that might cause issues updating the root node and move the bird/Sprite2D up? What if we have a more complicated object with more pieces? Do we need to tween all the pieces individually?
Thanks for the great course content. Looking forward to the next Lesson.10May. 15, 2024
General Q towards personal progressfledgerThus far I feel like I am doing pretty decently with some concepts, but for some I feel like they are not fully enforced yet. For example, I feel confident with some tween aspects, such as the properties and methods, but I get forgetful when it comes to the variable set up for the distance in a random radial direction. Also, I have had to peek at the solutions when it came to why my birds shadow was offset.
I feel as if this is a good module for me to stop before going into 7 to practice more.
Do you think it would be more beneficial to set up my own scenarios that are near identical to try to fully enforce these lessons? Or do you think repeating the lessons may be better to enforce the concepts?
Either way, I am not sure if it is just me, but I have a bit of a guilty conscience when I feel like I have look at previous code. Is it normal for beginners to do this at this stage? Am I being too hard on myself or right in line? I know these questions are a bit subjective, but I was curious at what you all may think. I am loving the progress I have made so far and found these lessons very helpful! I think I just want to have these points packed in before continuing on!
Thanks!30May. 14, 2024
Mouse exited signal triggering when entering a child‘s area HollowSo I decided to make a small complete game from the basis of what I learned up to this point, however I am facing an issue: I wanted to implement a small popup menu which appears when clicking on an item. The menu has two buttons and I wanted to have the menu disappear again when the player moves their mouse outside of the menu (area 2d)‘s collision polygon. The problem is that the mouse exited signal is also emitted when hovering over the buttons inside of the menu so the menu disappears when hovering over the buttons. How could I fix this so the buttons still work but do not trigger the menu‘s mouse exited signal?90Apr. 26, 2024
Bird hopping to 0,0 when trying final challenge with a mix of Animation Player and tween.TheKmankI am trying to mix the animation player (which is animating the sparrow sprite for the "jump") and a tween to move the bird in a random direction. However whenever I use start the scene, the bird's first jump will teleport it to 0,0 and I am sure why it is happening. Above is my code for the sparrow.
```gdscript
extends Area2D
@onready var sparrow: Sprite2D = $Sparrow
@onready var shadow: Sprite2D = $Shadow #not used in code
@onready var animation_player: AnimationPlayer = $AnimationPlayer
func _ready() -> void:
var hop_timer : Timer = Timer.new()
hop_timer.wait_time = randf_range(1.0, 3.0)
hop_timer.autostart = true
hop_timer.one_shot = false
hop_timer.timeout.connect(_on_timer_timeout)
hop_timer.start()
add_child(hop_timer)
func jump():
var random_angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle)
var random_distance := randf_range(10.0, 30.0)
var land_position := random_direction * random_distance
var tween := create_tween()
tween.tween_property(self, "position", land_position, 0.2)
animation_player.play("hop")
func _on_timer_timeout():
jump()
```40Apr. 25, 2024
Fireball shooting bird animation timing errorsHermundureWhile experimenting as my evening project, I placed both birds opposite to each other and attached a button for each bird. When this button is pressed, a Fireball.tscn, that has been dragged into the exported Array[PackedScene] is spawned.
This Fireball is flying towards the enemy bird and collides with the collision shape of the bird.
So far, so good. Everything works as expected but here the trouble starts (problems are described in the comments of the code section):
```gdscript
extends Area2D
@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D
var speed := 110
func _ready() -> void:
area_entered.connect(_on_area_entered)
animated_sprite_2d.animation_finished.connect(queue_free)
func _process(delta: float) -> void:
position.x += speed * delta
animated_sprite_2d.play("flight")
func _on_area_entered(area_that_entered: Area2D) -> void:
# The next line is supposed to stop the fireball at the current position to prepare the "explode" animation to be played
# in the place of impact. It does not work, the fireball keeps flying if I do not call queue_free() - how dangerous!
position = position
# The next line is supposed to change the "flight" animation to "explode" animation but by executing
# the code, it does nothing. The fireball resists my wish to change its animation.
animated_sprite_2d.play("explode")
# The next line is supposed to give the "explode" animation time to play and delete the child afterwards.
# It does not work, the fireball is deleted instantly when it hits the bird's collision shape.
animated_sprite_2d.animation_finished.emit()
# The next line is the only thing that works. It really prints it :-)
print_debug("Hit!")
```
I also drew and animated my own bird, it has a fire breathing animation now. The Fireball is supposed to come out after the fire breathing animation has finished but again I run into the problem that I can not get the animation_finished issue, which I described in the code block to work. As a result, the fireball comes out as soon as the button is pressed.:-(10Apr. 21, 2024
Bird Hop Challenge InconsistencyJayV3DHad a lot of fun doing the challenges - just wanted to mention that there seems to be an inconsistency in wording with the bird hop challenge.
In the challenge description, you mention to use the wait_time of the timer as the tween's duration:
**Challenge:Create a bird scene and script to make the bird hop around. To make the bird hop at regular intervals, you'll need to synchronize tweens with a timer's duration. To do this, you can use a `Timer` node and its `wait_time` property as a tween's duration.**
I'm not sure why you'd need to do this, unless you either wanted the bird to not wait between jumps, or if you wanted the jump to always be the same duration as the time it waited?
The suggested solution also does not do this, which could be confusing if you were struggling? Just an observation!10Apr. 20, 2024
Using CanvasGroup for bird solution?BobbyDevI'm grouping the shadow and the bird sprite within CanvasGroup, similar to some of the previous lessons using the ship.
Is setting the shadow position in the code and sprite_2d position using the tweens a better method or would these just be two different solutions? 10Apr. 20, 2024
Bird tween not moving...I'm sure it's simple.BoomsquallyI just started the last challenge and was playing around with tweening, trying not to go back to previous lessons, but I compared my current code to the spawning function in L6 and I just can't get why the bird ain't moving. I know it's looking a little crap, but shouldn't it even show some crappy movement?
extends Node2D
@onready var sprite : Sprite2D = $Sparrow
func _ready():
bird_hop()
func bird_hop() -> void:
var random_distance := randf_range(20.0, 40.0)
var random_angle := randf_range(0.0, PI * 2.0)
var random_direction := Vector2(1.0, 0.0).rotated(random_angle)
var tween := create_tween()
tween.tween_property(sprite, "position.y", -20.0, 1.0)
tween.tween_property(sprite, "position.y", 0.0, 1.0)
tween = create_tween()
tween.tween_property(sprite, "position.x", random_direction * random_distance, 1.0)
func _process(delta):
pass60Apr. 19, 2024
Question about the solution code for hopping birdPJWhat is the purpose of creating a new tween object in line 24? 40Apr. 10, 2024
Next stepsArtemaleThe whole course is amazing, Thank you! I've finally started to get clue with it. The next logical step seems to be building small games by my own.
I've started a few with some help of Youtube.
What I see clearly is that Godot 4 should have some version for html5 games as it was in 3.5
html5 games are ok to be simple and that's the place to actually let other people to play your games. Even if the game is not cool it's pretty much ok for a browser game to be released. It also let you get experience of people playing your games and in some cases even earn first money from game development.
I am considering to use Godot 3 for some time just to release a few html games. But it's kind of pain to ignore great Godot 4 tools like new tilemap system.
I know it migth not be the best place to write on it but I am just parasitizing on you guys actually read and answer these comments. Thank you!40Apr. 06, 2024
Input event processed on overlapping itemsSamukaveraIf I have two overlapping items and click on one of them, the two items will trigger the `_input_event()` function.
I've added the `get_viewport().set_input_as_handled()` trying to stop the event propagation, but with this, the input is processed in the bottom item, behind the clicked item.
I'm not finding an easy way to solve this... can you help me?150Apr. 04, 2024
Shared item.gd script doesn't work on different items. DargorSo I added code to the apple scene to make it shrink when clicking it. It worked, but when I clicked on the key and potion, nothing happened. I solved it by adding a new script to the potion and key, and using the exact same code. Maybe there's something about signals I'm not understanding. Any idea why this happened?30Apr. 03, 2024
bird shooChainSOVI wanted to program behaviour so that the bird jumps away from mouse cursor.
So I connected the on_mouse_entered signal like this, but was having very weird behaviour, sometimes the bird didn't jump at all, sometimes it did.
```gdscript
func _on_mouse_entered() -> void:
var vel = Input.get_last_mouse_velocity()
_jump(vel)
```
Noob eureka moment: your **jump function also needs to move the collision shape**, not only the sprite and shadow :-D
The bird only jumped when I moved the mouse cursor over its original position by accident
I still encounter some weirdness though, get_last_mouse_velocity sometimes returns different directions of mouse movement, even though I try to move my mouse steadily one direction :-O
is there a better approach?
Is there an option to read out InputEventMouseMotion.velocity inside my `_on_mouse_entered`function somehow?
Or is it only possible inside `_input_event` ?
I tried processing the logic in input_event, but only wanted it to do something if the signal mouse_entered fired, so I set some global variables in the signal function, but I feel like its a dirty hack and there should be a more elegant solution to this
```gdscript
var mouse_entered_birdspace:bool = false
var last_mouse_velocity:= Vector2(0.0,0.0)
func _on_mouse_entered() -> void:
mouse_entered_birdspace = true
last_mouse_velocity = Input.get_last_mouse_velocity()
```
```gdscript
func _input_event(viewport: Node, event: InputEvent, shape_index: int) -> void:
if not mouse_entered_birdspace:
return
if event is InputEventMouseMotion:
mouse_entered_birdspace = false
_jump(event.relative*30)
```
is there?
both InputEventMouseMotion.relative, InputEventMouseMotion.velocity and Input.get_last_mouse_velocity() sometimes produce jumps toward the mouse pointer though, not sure how to fix that.
here is how my jump looks like
```gdscript
@onready var shadow: Sprite2D = $Shadow
@onready var shadow_offset: Vector2 = shadow.position
func _jump(land_position:Vector2) -> void:
const HOP_DURATION := 0.25
const HALF_HOP_DURATION := HOP_DURATION / 2.0
var tween := create_tween().set_parallel()
tween.tween_property(sprite_2d, "position:x", land_position.x, HOP_DURATION)
tween.tween_property(shadow, "position", land_position + shadow_offset, HOP_DURATION)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
const JUMP_HEIGHT := 16.0
tween.tween_property(sprite_2d, "position:y", land_position.y - JUMP_HEIGHT, HALF_HOP_DURATION)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(sprite_2d, "position:y", land_position.y, HALF_HOP_DURATION)
collision_shape.position = land_position
wait_timer.wait_time = randf_range(1.0, 3.0)
tween.finished.connect(wait_timer.start)
```
does it make sense, to do something like
```gdscript
collision_shape.disabled = true
collision_shape.position = land_position
collision_shape.disabled = false
```
while changing position to prevent any more events from firing?60Mar. 30, 2024
Lesson Q&A
Use this space for questions related to what you're learning. For any other type of support (website, learning platform, payments, etc...) please get in touch using the contact form.