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.
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)`3110Apr. 04, 2024
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!97Mar. 25, 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. 94Apr. 12, 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!
13Apr. 20, 2024
a smooth flame toggle animationethrynAfter coding the toggle for flame visibility, I wasn't satisfied with how abrupt the transition between on/off was, so I decided to mess around with the shader to see if I could get smoother torch toggle animations.
After some experimentation I extracted some cutoff parameter in the shader code by inserting
`uniform float cutoff = 0.25;`
at the top, and changing a line to
`COLOR.a = step(cutoff, flame_shape * n * (UV.y * 1.25));`
Next I replaced the visibility toggle code with a tween for this new parameter:
```gdscript
if on:
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_EXPO)
tween.tween_property(flame.material, "shader_parameter/cutoff", flame_initial, 1.0)
else:
tween.tween_property(flame.material, "shader_parameter/cutoff", 1.0, 0.5)
```
where `flame_initial` is initialised as
`@onready var flame_initial : float = $Flame.material.get_shader_parameter("cutoff")`
The end result is a much smoother animation!
12Jan. 08, 2025
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
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?32Jun. 07, 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.32Apr. 17, 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
Input Detection Problemstrict-woodcockHi, I managed to make the torch flame show and hide with a mouse click, but I wanted to try adding an animation. Because my extinguish animation ends with a width of zero pixels, my solution doesn't involve actually toggling the visibility of the "Flame" node.
The extinguish animation works as expected, however when I try to reignite the flame with a second click it only works if I hold the mouse button down for a second before releasing it. The animation to bring the flame back then plays upon releasing the button.
I can't figure out what is causing this behavior. My torch script is as follows:
```gdscript
extends Node2D
@onready var flame: Sprite2D = $Flame
var flame_on := 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:
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed:
if flame_on:
var tween := create_tween()
tween.tween_property(get_node("Flame"), "scale", Vector2(0, 0.35), 0.25)
tween.finished.connect(toggle_torch_flame_state)
tween.finished.connect(flame_scale_down)
else:
var tween := create_tween()
tween.tween_property(get_node("Flame"), "scale", Vector2(0.28, 0.28), 0.25)
tween.finished.connect(toggle_torch_flame_state)
tween.finished.connect(flame_scale_reset)
func toggle_torch_flame_state() -> void:
flame_on = not flame_on
func flame_scale_down() -> void:
flame.scale = Vector2(0, 0.28)
func flame_scale_reset() -> void:
flame.scale = Vector2(0.28, 0.28)
```31Jan. 02, 2025
Help with Signals and Arrays (Torch challenge)jumbo-lionI wanted the torches to emit a signal when lit and connect it to the main scene. After a lot of trial and error, this is what I came up with, but it seems excessive to loop through and connect to each child when they all share the same script. Is there a better way of doing it? The first option I tried was to reference each child as a variable and connect them all separately, but that was too messy and not scalable.
mini_dungeon.gd
```gdscript
func _ready() -> void:
var torches := get_tree().get_nodes_in_group("torch")
for torch_child in torches:
torch_child.torch_lit.connect(_on_torch_lit)
torch.append(torch_child)
```
torch.gd
```gdscript
extends Area2D
signal torch_lit
@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)
flame.visible = false
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:
flame.visible = not flame.visible
_torch_lit()
func _torch_lit() -> void:
torch_lit.emit()
```
11Dec. 24, 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
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
SO AWSOME!!Jean RochaI create so many things besides the challenges haha, I got really excited trying to do my best on my own, I don't deny that there were times when it took me a while to do some challenges, but nothing that insisting and testing things leads you to understand how certain functions and Godot works, I feel like I opened my mind in these exercises, I'm excited for the next ones!10Feb. 15, 2025
Is there a placeholder for a variable?irritating-jayI'm wondering if there's something similar to putting 'pass' under a function but for variables. i find it helpful to write out the variable names first before figuring out what goes in them, but i don't want to see a whole bunch of errors while i do that. 10Feb. 15, 2025
Tried doing this with AIKalmaroSo, I used AI for helping me code these tasks and, I have to say that while the AI does a decent job, I couldn't have completed this without the knowledge I gained doing these tutorials. It would call nodes the wrong names and such, but I knew what to look for.
All in all, everything worked great and I feel like I was able to accomplish what would have taken me a little long on my own. I honestly wasn't sure where to start on the bird loll.
Thanks so much for these lessons so far. 50Feb. 14, 2025
Solution w/ Extra Functionality to Prevent Bird Jumping OffstagePingoTVHello! Here's the code I came up with to have the bird jump around at random intervals. I went about the solution in a different way in order to ensure that the bird never jumps outside of a specified area, and I also added functionality to make the bird flip horizontally when jumping to the right! (Shoutout @Shane for that idea)
```gdscript
extends Area2D
@onready var jump_area: CollisionShape2D = $JumpArea
@onready var bird: Sprite2D = $BirdSprite
@onready var shadow: Sprite2D = $Shadow
@onready var jump_timer = $Timer
func _ready() -> void:
jump_timer.timeout.connect(jump_handler)
func jump_handler() -> void:
jump_timer.wait_time = randf_range(1.0, 2.0)
var jump_bounds = jump_area.shape.extents
#You can get the outer boundaries of a rectangular collision shape w/ the .shape.extents member function
var random_x := randf_range(-jump_bounds.x, jump_bounds.x)
var random_y := randf_range(-jump_bounds.y, jump_bounds.y)
const JUMP_TIME := 0.4
const HALF_JUMP_TIME := 0.2
var tween := create_tween()
tween.set_parallel()
tween.tween_property(bird, "position:x", random_x, JUMP_TIME)
if random_x > bird.position.x:
tween.tween_property(shadow, "position", Vector2(random_x + 7, random_y + 18), JUMP_TIME)
else:
tween.tween_property(shadow, "position", Vector2(random_x - 7, random_y + 18), JUMP_TIME)
#The '-/+7' and '+18' are to account for the offset that exists because the shadow is placed off-center in the inspector
tween = create_tween()
var jump_height := 50.0
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(bird, "position:y", random_y - jump_height, HALF_JUMP_TIME)
tween.tween_property(bird, "position:y", random_y, HALF_JUMP_TIME)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.set_parallel()
tween.tween_property(shadow, "scale:x", 0.728, HALF_JUMP_TIME)
tween.tween_property(shadow, "scale:y", 0.455, HALF_JUMP_TIME)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.set_parallel()
tween.tween_property(shadow, "scale:x", 1.563, HALF_JUMP_TIME)
tween.tween_property(shadow, "scale:y", 0.977, HALF_JUMP_TIME)
#Flipping the sprite based on the direction that it's going to move (shoutout @shane again!)
#Notice that the same if/else check is done on the shadow in my version because I placed the shadow off-center
tween = create_tween()
if random_x > bird.position.x:
tween.tween_property(bird, "scale:x", -1.0, JUMP_TIME)
else:
tween.tween_property(bird, "scale:x", 1.0, JUMP_TIME)
```
For whatever reason, the tween that is supposed to scale the shadow under the bird does not seem to want to work. Any help with this would be greatly appreciated!
Otherwise, the code is fully functional. Hopefully someone sees this and finds it helpful!20Feb. 12, 2025
Did I miss where this challenge is?KalmaroHow do I start working on this, I don't see where to go.30Feb. 07, 2025
A solution with a shadow that grows larger and reduces alpha based on jump height. Also tween_callback with random delayWiseRat```gdscript
func hop_around() -> void:
var random_angle := randf_range(0.0, 2.0 * PI)
var random_direction := Vector2(1.0, 0).rotated(random_angle)
var random_distance := randf_range(10.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_parallel()
tween.tween_property(bird, "position:x", land_position.x, FLIGHT_TIME)
tween.tween_property(shadow, "position:x", land_position.x + (shadow.position - bird.position).x, FLIGHT_TIME)
var jump_height := randf_range(30.0, 60.0)
var jump_scale := jump_height / 60.0
# shadow tracking
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT_IN)
tween.tween_property(shadow, "position:y", land_position.y + (shadow.position - bird.position).y, FLIGHT_TIME)
# shadow size
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(shadow, "scale", Vector2(1.563, 0.977) * 2 * jump_scale, HALF_FLIGHT_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(shadow, "scale", Vector2(1.563, 0.977), HALF_FLIGHT_TIME)
# shadow opacity
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(shadow, "modulate:a", 0.3 / jump_scale, HALF_FLIGHT_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(shadow, "modulate:a", 1, HALF_FLIGHT_TIME)
# bird vertical
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
tween.tween_property(bird, "position:y", land_position.y - jump_height, HALF_FLIGHT_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(bird, "position:y", land_position.y, HALF_FLIGHT_TIME)
tween.tween_callback(hop_around).set_delay(randf_range(1, 3.0))
```
Not the cleanest code, but I'm a bit chuffed at the shadow behaviour so I thought I'd share.
Also at the end I use "tween_callback" which I saw at the top of the tween documentation looked up in L8. Is this just a connection to the finished playing signal +/- a throwaway timer under the hood? Or is it doing something mysterious and perhaps undesirable so using the signals is better?10Feb. 06, 2025
Shadow acting strange and lagging behind birdluminous-wrenHi, I'm comparing my code to the solution. I initially arrived at a solution where I set the shadow's position to the same as the landing position, but that ended up with the shadow shifting to directly behind the bird. Next, I tried offsetting the shadow with the set_offset function, and the shadow is generally staying under the bird, but sometimes, it's lagging behind the bird, and I am not sure what I am doing wrong. I know my code seems inefficient in places and I can make it more readable, but I can't figure out the fundamental issue with the shadow lagging behind the bird.
Here's what I ended up doing, and the results are okay, but not great. Thank you for the help!
```gdscript
extends Node2D
@onready var hop_timer: Timer = $Timer
@onready var shadow: Sprite2D = $Shadow
@onready var sparrow: Sprite2D = $Sparrow
var HOP_TIME: float = randf_range(0.5, 1.0)
var HALF_HOP_TIME: float = HOP_TIME / 2.0
func _ready() -> void:
hop_timer.set_one_shot(true)
hop_timer.wait_time = randf_range(0.5, 3.0)
hop_timer.start()
get_node("Timer").timeout.connect(_on_timer_timeout)
shadow.set_offset(Vector2(-6, 18))
func _on_timer_timeout() -> void:
hop()
func hop() -> void:
var random_position: Vector2 = Vector2(1.0, 0.0).rotated(randf_range(0, 2 * PI)) * randf_range(10.0, 20.0)
var random_hop_height: float = randf_range(20.0, 30.0)
var tween : Tween = create_tween()
tween.set_parallel()
#bird is hopping on timer, shadow not working as intended
tween.tween_property(get_node("Sparrow"), "position:x", random_position.x, HOP_TIME)
tween.tween_property(get_node("Shadow"), "position", random_position, HOP_TIME)
tween = create_tween().set_parallel()
tween.set_trans(Tween.TRANS_QUAD)
tween.tween_property(get_node("Sparrow"), "position:y", random_position.y - random_hop_height, HALF_HOP_TIME)
tween.tween_property(get_node("Sparrow"), "position:y", random_position.y, HALF_HOP_TIME)
hop_timer.wait_time = randf_range(0.5, 3.0)
HOP_TIME = randf_range(0.5, 1.0)
hop_timer.start()
```30Jan. 30, 2025
Tweening torch flame Scale and not allowing spam acitvation of torch flame tweens quarterly-snakeI don't Know how efficient of a solution this is but it was the one I found and it seems to work. Allows the torch's flame to be turned off and on with a scaling tween while also not allowing the flame's tween animation to be interrupted casuing buggy visuals and behaviour. The timer is set for a 0.5 sec wait time to match the tween and both the flame sprite and the timer are held in a Node2D in the hierarchy I called FlameHolder so that the flame dies out from the middle of the torch's head instead of someplace above it.
```gdscript
extends Node2D
@onready var flame: Sprite2D = $FlameHolder/Flame
@onready var flameHolder: Node2D = $FlameHolder
@onready var timer: Timer = $FlameHolder/Timer
var can_be_lit: 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)
timer.timeout.connect(set_can_be_lit)
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
var left_mouse_click_on : bool = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if left_mouse_click_on and can_be_lit:
toggle_flame_anim()
timer.start()
can_be_lit = false
func toggle_flame_anim() -> void:
var tween : Tween = create_tween()
if flame.visible:
tween.tween_property(flameHolder, "scale", Vector2(0,0), 0.5)
tween.finished.connect(toggle_flame)
else:
toggle_flame()
tween.tween_property(flameHolder, "scale", Vector2(1,1), 0.5)
func toggle_flame() -> void:
if flame.visible:
flame.visible = false
else:
flame.visible = true
func set_can_be_lit() -> void:
can_be_lit = true
```10Jan. 29, 2025
Super streamlined and handy codelikely-giraffeLess a question and more something I found on Google when looking to trim down this code:
```gdscript
if flame.visible == true:
flame.visible = false
elif flame.visible == false:
flame.visible = true
```
Instead I was able to put:
```gdscript
flame.visible = !flame.visible
```
Which achieved the same result but in a singular line instead. I went looking because I was confident there'd be some way to invert a bool value and luckily there is!
I guess my question would be, is there a reason I wouldn't use this? Are there any inherent risks or mistakes I could fall in to?
PS. Pressing enter 3 times like the prompt suggests to get out of a code block when commenting doesn't work. I had to hold the down arrow then press Enter (hope that helps somebody else)10Jan. 28, 2025
Too Few Arguments (Problem Solved)PurpleHuesOn the challenge- "Make the items play an animation before freeing them." I'm having trouble figuring out what to put at the last argument. I've tried using search help, and look through the script for reference, but all I could think of was using one of the variables in the play floating function. I put- tween.tween_property(sprite_2d, "scale", Vector2(0, 0)...?)70Jan. 21, 2025
Access other scenes via codeWurdlHello, i'm currently at M6 L9 in the course and love it so far.
If have a question that cant see to find a answer for. Maybe i'm phrasing it wrong.
My current scene structure is like this:
- Root
**MiniDungeon
***Background
***Chest
***Chest2
***Chest3
***Control
The Control Scene is a UI Scene where i have 3 labels as a child that should display the collected item count for each of the three items in this module. (apple, key and potion)
How would i acces these labels inside the item.gd?
Is there a "best" way to do that?
I cant seem to figure it out
This is my item.gd:
```gdscript
extends Area2Dfunc _ready() -> void:
play_floating_animation()
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, 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)
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
if event.is_action_pressed("left_click"):
queue_free()
#update Label?
```
```gdscript
extends Node
var
items := { "apples" = 0, "keys" = 0, "potions" = 0}
func _ready() -> void:
update_labels()
func add_item(item_name: String, amount: int) -> void:
if item_name in items:
items[item_name] += amount
update_labels()
func update_labels() -> void:
pass
```
inside my player_interface.gd (the UI Scene) i want to update the count and labels
Thank you :)20Jan. 18, 2025
My attempt at the challenge.soft-mosquitoI followed the suggestions but wanted to add some flair. I made it so when i click the torch to turn off the flame I also added a little animation for some feedback:
```gdscript
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:
var tween := create_tween()
tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
tween.tween_property($".","scale",Vector2(1.25, 1.25), 0.1)
tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tween.tween_property($".","scale", Vector2(1, 1), 0.1)
if flame.visible == true:
flame.visible = false
elif flame.visible == false:
flame.visible = true
```
I made the torch scale up and down on the click. I feel like there is a better way adjusting the visibility of the flame besides if statements. Next instead of adding the birds to the scene, I wanted to allow the "player" to add as many as they wanted and also where. I first set up a button to toggle the ability to click and spawn a bird. I had created another control node in the main scene and added a button. It was quite embarrassing to say how long it took me to figure out how to properly use the toggle function. I kept trying to reference the toggle signal from it but it would always report back as true( turns out it signals that the button is in togglable mode, I think, still don't know). Finally I figured out to use the pressed() signal and set up my input function with that:
```gdscript
func _process(delta: float) -> void:
if $Interface/Button.button_pressed == true and Input.is_action_just_pressed("right_click"):
click_position = get_global_mouse_position()
print(click_position)
var bird_instance :Area2D = bird_scene.instantiate()
bird_instance.position = click_position
add_child(bird_instance)
```
I got it to spawn the birds where I clicked and had some fun with changing the "Input.is_action_just_pressed" to "Input.is_action_pressed" and would just hold my mouse down and spawn a few hundred birds!. I had to change the button the would spawn the birds from left mouse click to right mouse click as clicking the button to toggle it would also spawn a bird right on the button as in now it fulfills the requirements of the if statement. Changing it was the lazy fix. Probably could try some sort of area node to interact with the mouse to prevent it from running while the mouse is on the button. Next I animated the birds. I made them hop at different intervals, adjusted the distance they hop based on the height they jumped (it seems to work but might be janky), change the shadow size based on the height of the jump as well (same, might be janky) and also rotate the sprite based on the change of direction for each hop. I did just use the offset for the shadow to make it stay under the bird and since I only allow the bird to move on the x axis it seems to work well.
```gdscript
extends Area2D
@onready var timer := $Timer
func _ready() -> void:
_timer()
func _timer() -> void:
timer.wait_time = randi_range(2,6)
timer.start()
timer.timeout.connect(_hop)
func _hop() -> void:
#Main variables
var bird := $Sprite2D
var shadow := $Shadow
#Movement variables and tweening animation
var current_position : Vector2 = $Sprite2D.position
var shadow_position : Vector2 = $Shadow.position
var shadow_inital_scale : Vector2 = $Shadow.scale
var tween := create_tween()
var random_height := Vector2(0, randf_range(5.0,20.0))
var shadow_size := Vector2(shadow_inital_scale.x - random_height.y * .05,shadow_inital_scale.y - random_height.y * .05)
var direction := Vector2(randi_range(-1,1), 0)
var distance := random_height.y * 1.5
var land_position := current_position + direction * distance
var shadow_land_position :Vector2 = shadow_position + direction * distance
tween.tween_property(bird,"offset",-random_height, 0.15).set_ease(Tween.EASE_OUT)
tween.parallel().tween_property(bird, "position", land_position, .15).set_ease(Tween.EASE_OUT_IN)
if land_position.x > current_position.x:
$Sprite2D.flip_h = true
$Shadow.offset = Vector2(4,0)
else:
$Sprite2D.flip_h = false
$Shadow.offset = Vector2(-4,0)
tween.parallel().tween_property(shadow,"position", shadow_land_position,.2).set_ease(Tween.EASE_OUT_IN)
tween.parallel().tween_property(shadow,"scale", shadow_size,.15).set_ease(Tween.EASE_OUT)
tween.tween_property(bird,"offset",Vector2(0,0), 0.15).set_ease(Tween.EASE_IN)
tween.parallel().tween_property(shadow,"scale", shadow_inital_scale,.15)
tween.finished.connect(_timer)
```
I did seem to run into an issue that even using the line "tween = create_tween()" it would seem to have the last tween animation for the offset ( line 40) overrule the other animation and the bird would just slide across the screen. But I found if you add the "tween.parallel" before the tween code, would allow it to run with the previous tween and not effect all the tweens. Hope this might help anyone with some ideas.
10Jan. 17, 2025
Incremental Counter for Items Spawned as SiblingsPimpyShorstockingHello! I was hoping I could get some help and thank you ahead of time!
I was attempting to create an increment counter similar to how we did it in an earlier lesson, though I know that there are significant differences between the ship and here.
Earlier on in M6 L6 when we created random loot, I wanted to find a way have the chests disappear. It took a bit but I added a sibling when an item is spawned rather than a child. This has turned out to be a bit of a headache to work around for making this increment.
So here's what I have done so far:
- Each item (Apple (Food in my case), Key, and Potion) are in their own groups.
- I created a Food Count, Potion Count, and Key Count label in the game scene.
- A chest is clicked and spawns random items as siblings on the main scene tree.
- I can click on the items and they disappear as per an earlier exercise in M6 L9.
Here's my code I am trying to use in the game scene (mini_dungeon.tscn) to increment the items.
```gdscript
extends Node2D
@onready var food_count: Label = $FoodCount
@onready var potion_count: Label = $PotionCount
@onready var key_count: Label = $KeyCount
var potion_counter = 0
var key_counter = 0
var food_counter = 0
func _ready() -> void:
var get_food = get_tree().get_nodes_in_group("food")
var get_keys = get_tree().get_nodes_in_group("keys")
var get_potions = get_tree().get_nodes_in_group("potions")
for food in get_food:
if food.tree_exited:
_on_loot_pickup(food)
for potion in get_potions:
if potion.tree_exited:
_on_loot_pickup(potion)
for key in get_keys:
if key.tree_exited:
_on_loot_pickup(key)
func _process(delta: float) -> void:
pass
func _on_loot_pickup(loot) -> void:
if loot.is_in_group("keys"):
key_counter += 1
key_count.text = "x " + str(key_counter)
elif loot.is_in_group("food"):
food_counter += 1
food_count.text = "x " + str(food_counter)
elif loot.is_in_group("potions"):
potion_counter += 1
potion_count.text = "x " + str(potion_counter)
```
I know that where the logic currently is doesn't work, because the Chests have not spawned the sibling items until after they are clicked. I did manage to get it to KINDA work when I checked for them in the `_process` function, however when the items spawn, they increment PER frame and not on click, which is not ideal.
I tried using something like the `tree_exited` signal above as well as in the `_on_loot_pickup` function to check for the items before working to increment them, but I wasn't able to fully grasp the logic I need here and was stuck for quite awhile. I will keep plucking away at it but any help would be much appreciated!
Here's additional code for how I set up my chest if that helps at all
```gdscript
extends Area2D
@onready var canvas_group: CanvasGroup = $CanvasGroup
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@export var timer: float = 0.0
@export var possible_items: Array[PackedScene] = []
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
mouse_entered.connect(_on_mouse_entered)
mouse_exited.connect(_on_mouse_exited)
get_viewport().physics_object_picking_sort = true
get_viewport().physics_object_picking_first_only = true
canvas_group.material.set_shader_parameter("line_thickness", 3.0)
func _on_mouse_entered() -> void:
var tween := create_tween()
tween.tween_method(set_outline_thickness, 3.0, 6.0, timer)
func _on_mouse_exited() -> void:
var tween := create_tween()
tween.tween_method(set_outline_thickness, 6.0, 3.0, timer)
func set_outline_thickness(new_thickness: float) -> void:
canvas_group.material.set_shader_parameter("line_thickness", new_thickness)
func _input_event(viewport: Viewport, event: InputEvent, shape_index: int):
var event_is_mouse_click = (
event is InputEventMouseButton and
event.button_index == MOUSE_BUTTON_LEFT and
event.is_pressed()
)
if event_is_mouse_click:
open()
func open() -> void:
animation_player.play("open_chest")
input_pickable = false
if possible_items.is_empty():
return
for index in range(randi_range(1,3)):
_spawn_random_item()
func _spawn_random_item() -> void:
const FLIGHT_TIME := 0.4
const HALF_FLIGHT_TIME := FLIGHT_TIME / 2.0
var loot_item: Area2D = possible_items.pick_random().instantiate()
add_sibling(loot_item)
loot_item.global_position = global_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(60.0, 120.0)
var land_position := global_position + random_direction * random_distance
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, "global_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(30.0, 80.0)
tween.tween_property(loot_item, "global_position:y", land_position.y - jump_height, HALF_FLIGHT_TIME)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(loot_item, "global_position:y", land_position.y, HALF_FLIGHT_TIME)
tween.tween_callback(queue_free)
```50Jan. 16, 2025
How to emit signal from spawned items to MiniDungeon nodesoft-mosquitoHi. I have been trying to experiment with adding a little counter in the main scene and am trying to get the spawned items to emit a signal when clicked on and for the MiniDungeon Node2d to "see" the signal and then run a function to update the counters on screen. I just cant seem to find the best answer to do so. I have created a custom signal in the item.gd and have it emit when I click on it. As it is not part of the original scene, I don't see it when i am trying to write the connect code in a script I made for the Node2d scene "MiniDungeon".
```gdscript
# 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.
extends Area2D
signal item_collected
func _ready() -> void:
play_floating_animation()
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, 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)
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:
var tween := create_tween()
var sprite_2d : Sprite2D = get_node("Sprite2D")
tween.tween_property(sprite_2d,"scale", Vector2(0,0), 0.5)
tween.finished.connect(_collect_apple)
func _collect_apple() -> void:
emit_signal("item_collected")
queue_free()
```
Here is the code for the items. I have also changed the groups for each item to their own already so I think all I need left is to grab the signal on the other end and tell it to update the item counters.60Jan. 15, 2025
my experimentssqwirexmy bird, I added an x flip when she jumps and smoothly move the shadow behind her:
```gdscript
func on_timer_timeout() -> void:
timer.wait_time = randf_range(1.0, 2.0)
var random_angle := randf_range(0.0, 2 * PI)
var random_duration := Vector2(1.0, 0.0).rotated(random_angle)
var random_distance := randf_range(20.0, 30.0)
var land_position := random_distance * random_duration
var flip : bool = bird.flip_h
var offset_change := 9
if flip == true:
offset_change *= -1
const FLIGHT = 0.5
const HALF_FLIGHT = FLIGHT / 2
var tween := create_tween()
tween.set_parallel()
tween.tween_property(bird, "position:x",bird.position.x + land_position.x, FLIGHT)
tween.tween_property(bird, "flip_h" ,not flip, FLIGHT)
tween.tween_property(shadow, "offset:x" ,shadow.offset.x + offset_change, FLIGHT)
tween = create_tween()
tween.set_trans(Tween.TRANS_QUAD)
tween.set_ease(Tween.EASE_OUT)
var jump_height := randf_range(30.0, 50.0)
tween.tween_property(bird, "position:y", bird.position.y + land_position.y - jump_height, HALF_FLIGHT)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(bird, "position:y", bird.position.y + land_position.y, HALF_FLIGHT)
```
added a smooth disappearance to the chest after opening and after the animation I release its sprite, and after picking up all the items, I release the entire node, and also added a random selection of two items to each chest:
```gdscript
tween.tween_property(canvas_group, "modulate:a", 0.0, 0.5)
tween.finished.connect(canvas_group.queue_free)
func _process(_delta: float) -> void:
if chest.get_child_count() == 2:
queue_free()
for current_index in range(randi_range(1, 3)):
var delete_item = possible_items.pop_at(randi_range(0,2))
_spawn_random_item()
possible_items.append(delete_item)
```
added the disappearance of an item when clicked and, using groups and global variables, wrote these values into a label (released the collision immediately when clicked, so that it was impossible to spam and farm several clicks on one item, and also turned off particles and reduced their lifetime, lower than disappearance item so that they do not suddenly disappear:
```gdscript
func _input_event(_viewport: Viewport, event: InputEvent, _shape_idx: int) -> void:
var event_is_mouse_click : bool = (
event is InputEventMouseButton and
event.is_pressed() and
event.button_index == MOUSE_BUTTON_LEFT
)
if event_is_mouse_click:
collision_shape_2d.queue_free()
var tween := create_tween()
var disered_scale := Vector2(0.0, 0.0)
tween.tween_property(item, "scale", disered_scale, 0.6)
sparks.emitting = false
sparks.lifetime = 0.5
tween.finished.connect(queue_free)
match item.get_groups()[0]:
"poison":
Global.poison_count+=1
"key":
Global.key_count+=1
"apple":
Global.apple_count+=1
```
UI:
```gdscript
extends Control
func _process(_delta: float) -> void:
$AppleLabel.text = "x" + str(Global.apple_count)
$KeyLabel.text = "x" + str(Global.key_count)
$PoisonLabel.text = "x" + str(Global.poison_count)
```
when there are no chests left, the chest spawns every second and stops again until all the items are opened and collected, and the chest also appears smoothly (-384,305 are the coordinates of the beginning of the screen, so you have to add and subtract them accordingly so that the chests do not spawn behind the screen):
```gdscript
func on_timer_timeout() -> void:
if mini_dungeon.get_child_count() == 7:
var chest_scene = CHEST.instantiate()
var viewport_x : float = get_viewport_rect().size.x
var viewport_y : float = get_viewport_rect().size.y
var margin_spawn := 300.0
var spawn_x := randf_range(margin_spawn, viewport_x - margin_spawn)
var spawn_y := randf_range(margin_spawn, viewport_y - margin_spawn)
chest_scene.position.x = spawn_x - 384
chest_scene.position.y = spawn_y - 305
chest_scene.modulate.a = 0.0
add_child(chest_scene)
var tween := create_tween()
tween.tween_property(chest_scene, "modulate:a", 1.0, 0.4)
```
Finally, torch:
```gdscript
func _input_event(_viewport: Viewport, event: InputEvent, _shape_idx: int) -> void:
var event_is_mouse_click : bool = (
event is InputEventMouseButton and
event.is_pressed() and
event.button_index == MOUSE_BUTTON_LEFT
)
if event_is_mouse_click and flame.visible != false:
flame.visible = false
elif event_is_mouse_click:
flame.visible = true
```
I would like to receive criticism on what points could have been done differently or everything was great, I hope you like it.40Jan. 06, 2025
loot items using the same copy paste codekindly-falconI know something is wrong, because for apple, key and potion im using the same code. I could copy paste it.
instead i should write the code once, and then make all 3x items inherit this code.
but how do i attach Apple.gd to the Key.tscn ?30Dec. 17, 2024
This was fun. Here's some more challenges to try.Alphagold3Try making it so that you can turn off the torches by wiggling the mouse over it but only if you wiggle it fast enough (IE simulating trying to blow out the torch by waving a fan at it). It's a bit silly but it was a fun exercise.
Try making it so that when you collect items it displays a count of each type of item separately (IE 2 Apples, 3 Keys, and 4 Potions).
Try making the chest close itself and refill with items after some random delay.10Dec. 13, 2024
All items disappearing when 1 is clickedadvanced-rabbitI was having an issue with every item disappearing when 1 was clicked. I found the solution in the community comments, which is to add "get_viewport().set_input_as_handled()" to the input_event method. I did not see that step in any of the tutorials so I think that you may want to add that for others. (Unless I overlooked this somewhere!)
30Nov. 25, 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?OldManBradHi,
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 OldManBradHi,
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?
Thanks60Jun. 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!50May. 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.