Looks like you're not logged in

Login or Register to continue

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.

  • Bravobardcore-pandaI want to give kudos after every lesson but I worry that it would flood the Q&A section. Since there are no comments yet, I'll do it on this one! You guys have accomplished an amazing pedagogical feat with this course. I teach coding to kids, so I can tell how much thought you've put into everything. All the exercises, the order in which you introduce ideas, the practices, the challenges you give us, the extra resources you point us to, teaching the programmer's mindset, little bits of humor... Keep up the great work Nathan and team! 1 32 May. 20, 2024
  • Challengemarinaton```gdscript extends Control #Control as the root node, with the script attached #There is also ColorRect node in the Scene Dock #BBcode is enabled @onready var texture_rect = %TextureRect @onready var texture_rect_2 = %TextureRect2 @onready var texture_rect_3 = %TextureRect3 @onready var rich_text_label = %RichTextLabel @onready var timer = %Timer var dialogue_items: Array[String] = [ "[center]Long ago, there existed a kingdom where a golden power lay hidden.[/center]", "[center]It was a prosperous land blessed with forests, tall mountains, and peace.[/center]", "[center]But one day a man of great evil found the golden power and took it for himself...[/center]", "[center] ....................................................................[/center]", "[center]This boy, who traveled through time to save the land, was known as the Hero of Time[/center]", "[center]The boy's tale was down through generations until it became legend...[/center]", ] func _ready() -> void: timer.autostart = false timer.wait_time = 10.0 timer.start() fading_1() image_slide_1() show_text() timer.timeout.connect(func() -> void: current_item_index += 1.0 if current_item_index == dialogue_items.size(): get_tree().quit() else: fading_2() ) func show_text() -> void: rich_text_label.text = dialogue_items[current_item_index] var text_wait: float = rich_text_label.get_parsed_text().length() / 10.0 timer.wait_time = text_wait func fading_1() -> void: var tween := create_tween() tween.set_trans(Tween.TRANS_SINE) tween.set_ease(Tween.EASE_OUT) rich_text_label.modulate.a = 0.0 tween.tween_property(rich_text_label, "modulate:a", 1.0, 0.4).set_delay(2) func fading_2() -> void: var tween := create_tween() tween.set_trans(Tween.TRANS_EXPO) tween.set_ease(Tween.EASE_OUT) rich_text_label.modulate.a = 1.0 tween.tween_property(rich_text_label, "modulate:a", 0.0, 0.4).set_delay(2) tween.finished.connect(func() -> void: timer.start() fading_1() show_text() if dialogue_items[2] == dialogue_items[current_item_index]: image_fade_1() if dialogue_items[4] == dialogue_items[current_item_index]: image_fade_2() image_slide_2() ) func image_slide_1() -> void: var tween := create_tween() tween.tween_property(texture_rect, "position", Vector2(0, 0), 25) func image_slide_2() -> void: var tween := create_tween() tween.tween_property(texture_rect_3, "position", Vector2(-700, 0), 25) func image_fade_1() -> void: var tween_fade := create_tween() tween_fade.tween_property(texture_rect, "modulate:a", 0, 0.8) func image_fade_2() -> void: var tween_fade := create_tween() tween_fade.tween_property(texture_rect_2, "modulate:a", 0, 0.8) ``` I tried to replicate the intro slideshow from the Zelda Wind Waker video and keep the code size as small as I could. [https://imgur.com/a/Mey5n04](https://imgur.com/a/Mey5n04) Initially, I thought I could use Timer's **time_left** property, so that **fading_2** could start before timer reaches 0, but it didn't work as intended, so I used another way to complete the challenge. 1 1 May. 31, 2024
  • Scale/Slideshow ChallengeAJ StudiosI took on both challenges and wanted to show you guys the results I got. It's not ideal or the best but at least I was able to do it. Here's a link to the video: [https://youtu.be/UMWJ57LwDZo](https://youtu.be/UMWJ57LwDZo) 6 1 May. 29, 2024
  • Challenge 2: too big?davidakThis feels like too much work for a challenge. While i finished the other challenges in a few minutes by adding like 3 lines of code, this one feels like it will take half an hour, maybe even more than the average lesson! If i understand correctly, i would have to create a new scene and start from scratch. Adding many nodes and code. Then look for images and decide which one to use. One could be creative here and invent a story, but i don't think it's worth the effort for a challenge. So i would only create a demo. Do i understand the scope correctly? I feel overwhelmed by this challenge, because i don't know where to begin and feel discouraged, because the hints are not really helpful for solving the challenge. I want to do every challenge, to get the most out of the course, but this one seem to be too much for me. I left this challenge out and continued with the next 3 lessons. Could you add more hints about the high-level steps to solve the challenge? 1 0 Sep. 18, 2024
  • Challenge 1: instructions uncleardavidak> What can you do to make the animation even more attractive? That's kind of a big task. I haven't done a lot of animations and haven't seen a lot of cartoons to know how it's usually done. > Can you use scale to give Sophia a bit of squash and stretch? Sure i can, but how exactly should the animation look like? Can you maybe show an example video, as hint? I made her squash at the end when coming in. Hope that's good enough. ```gdscript tween.tween_property(body, "position:x", 0.0, 0.3) tween.parallel().tween_property(body, "modulate:a", 1.0, 0.2) tween.parallel().tween_property(body, "scale", Vector2(0.9, 1.0), 0.2) tween.tween_property(body, "scale", Vector2(1.0, 1.0), 0.1) ``` 1 0 Sep. 17, 2024
  • Intro slideshow that tells a story (Zelda-like) (Solution)HarbingerShThis is a rather complicated solution... So I tried to copy the Zelda intro slideshow and that's what I came up with! First, we set up a new scene [https://prnt.sc/jtlMUy1HSHwl](https://prnt.sc/jtlMUy1HSHwl) your scene should look like this The `Background` should be a Full Rect black color, whereas the `ImageRect` should look something like this [https://prnt.sc/H9a-NmMIo2bg](https://prnt.sc/H9a-NmMIo2bg) And most important is to make sure that the `ImageRect` Clip Children property under the visibility section is set to Clip Only [https://prnt.sc/ZUHhEtnpf25T](https://prnt.sc/ZUHhEtnpf25T) and the `Image` node is set to Full Rect and Expand Mode is set to `Fit height Proportional` and the Stretch Mode to `Keep Aspect Centered` [https://prnt.sc/vi3-ocRtZxtM](https://prnt.sc/vi3-ocRtZxtM) the `IntroText` label should also be a Full Rect and aligned to the center also make sure to set the modulate to be transparent as the default [https://prnt.sc/D5kmjvZrpJma](https://prnt.sc/D5kmjvZrpJma) same with the `Caption` label keep it centered and anchor it to the bottom of the screen but don't change the modulate property keep it as it is [https://prnt.sc/6kcYAadY5wK2](https://prnt.sc/6kcYAadY5wK2) now we can start coding first create a new Resource by right clicking on the `lessons` file in the Filesystem dock then Create new -> Script [https://prnt.sc/Q2hHBh3nM-VS](https://prnt.sc/Q2hHBh3nM-VS) make sure to set the inheritance to `Resource` and give it a name like `Caption` [https://prnt.sc/Hvg4aVCLtjSH](https://prnt.sc/Hvg4aVCLtjSH) this is the code for the new `Resource` ```gdscript class_name Caption extends Resource enum DIRECTIONS { Down, Up, Right, Left } @export var image: Texture2D = null @export var scroll_direction: DIRECTIONS = DIRECTIONS.Down @export_multiline var text: Array[String] = [] #Change that to whatever suits you! const image_base_divion: float = 50.0 func _on_get_duration_for_image() -> float: var image_size: float = 0.0 match scroll_direction: DIRECTIONS.Down, DIRECTIONS.Up: image_size = image.get_height() DIRECTIONS.Right, DIRECTIONS.Left: image_size = image.get_width() return image_size / image_base_divion func _on_get_duration_for_one_caption() -> float: return _on_get_duration_for_image() / text.size() ``` then, go back to the `IntroSlideshow` node (the root node) and make new script and add this code also ```gdscript class_name Slideshow extends Control @onready var intro_label: Label = %IntroText @onready var image: TextureRect = %Image @onready var caption: Label = %Caption @export_multiline var intro_text: String = "" @export_range(1.0, 10.0) var intro_text_duartion: float = 5.0 @export var slides: Array[Caption] = [] func _ready() -> void: await _on_display_intro_text() _on_display_slides() func _on_display_intro_text() -> void: intro_label.set_text(intro_text) var tween: Tween = create_tween() tween.tween_property(intro_label, "modulate", Color(1.0, 1.0, 1.0, 1.0), 1.2) await get_tree().create_timer(intro_text_duartion).timeout tween = create_tween() tween.tween_property(intro_label, "modulate", Color(1.0, 1.0, 1.0, 0.0), 1.2) await tween.finished func _on_display_slides() -> void: for slide: Caption in slides: image.set_texture(slide.image) image.scale = Vector2.ONE image.pivot_offset = image.texture.get_size() / 2 var direction: String = "" var end_position: float = 0.0 match slide.scroll_direction: slide.DIRECTIONS.Down: direction = "position:y" image.position.y = -image.texture.get_height() slide.DIRECTIONS.Up: direction = "position:y" image.position.y = 0 end_position = -image.texture.get_height() slide.DIRECTIONS.Right: image.scale = Vector2(1.2, 1.2) direction = "position:x" image.position.x = image.get_size().x * 0.1 end_position = -(image.get_size().x * 0.1) slide.DIRECTIONS.Left: image.scale = Vector2(1.2, 1.2) direction = "position:x" image.position.x = -(image.get_size().x * 0.1) end_position = image.get_size().x * 0.1 var tween: Tween = create_tween() tween.tween_property(image, direction, end_position, slide._on_get_duration_for_image()) var caption_duration: float = slide._on_get_duration_for_one_caption() var half_caption_duration: float = caption_duration / 2.0 for text: String in slide.text: caption.modulate = Color(1.0, 1.0, 1.0, 1.0) caption.visible_ratio = 0.0 caption.set_text(text) var new_tween: Tween = create_tween() new_tween.tween_property(caption, "visible_ratio", 1.0, half_caption_duration) await get_tree().create_timer(caption_duration).timeout new_tween = create_tween() new_tween.tween_property(caption, "modulate", Color(1.0, 1.0, 1.0, 0.0), 0.2) await new_tween.finished ``` in the inspector you will see all things to make your intro, like the Intro text and it's duration and most importent the Slides property which now you can click on it and add a new `Caption Resource` which contain a Image to display and the Scroll Direction and a Array of Strings to add as many Captions as you need! [https://prnt.sc/eB-pweat_hWm](https://prnt.sc/eB-pweat_hWm) now add a new image and select the direction and add captions [https://prnt.sc/j-CIHC4SnTL0](https://prnt.sc/j-CIHC4SnTL0) now run the scene and done! 👍 you could imporve it and add sounds effects that would be a good practice for you! 1 0 Sep. 08, 2024
  • (improved) My fix to tween animation problems when advancing dialogue quickly.MatejOne thing that bothered me was, that upon advancing the dialogue quickly, *the animations wouldn't restart from the baseline* (or in other words, *the initial position we want to tween from*). I looked into **Tween** and **PropertyTweener** documentation, and thought that maybe '**.from_current()**' (a method available to PropertyTweener) could help? And it does work. At least for this project so far. No matter how quickly I advance the dialogue, the tweens will always start from the values that are located under '**# INITIAL VALUES**' (see comment in the code below). This also works for the tween that is animating the text appearing on the screen. ```gdscript ## Animate the body sliding in upon advancing/rewinding dialogue. func slide_in() -> void: # Setting up the tween. var tween := create_tween() tween.set_trans(Tween.TRANS_QUAD) tween.set_ease(Tween.EASE_OUT) tween.set_parallel() # INITIAL VALUES body.position.x = 120.0 body.modulate.a = 0.0 body.scale.x = 0.98 body.scale.y = 1.04 # Animating the object properties to desired values. tween.tween_property(body, "position:x", 0.0, 0.8).from_current() tween.tween_property(body, "modulate:a", 1.0, 0.4).from_current() tween.tween_property(body, "scale:x", 1.0, 0.2).from_current().set_trans(Tween.TRANS_BOUNCE) tween.tween_property(body, "scale:y", 1.0, 0.5).from_current().set_trans(Tween.TRANS_BOUNCE) #... func show_text() -> void: #... tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration).from_current() #... slide_in() ``` Of course another solution is to somehow disable the buttons until the longest tween is finished (including the text we're animating in this project). **HOWEVER!** - Another problem arose! The audio playback would still sometimes stop prematurely and/or not start from the beginning and then stop. Again, this time when we advance through the dialogue before '**tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration)**' finishes animating. I've managed to find a solution for that: instead of stopping the audio playback with '**tween.finished.connect()**' - stop it with a **'timer.timeout()'** signal and use another **custom signal** to stop the old timer when we advance dialogue faster than '**text_appearing_duration**' (because I use this variable for the '**timer.wait_time()**'). I know that on its own, the explanation isn't descriptive enough, so here are the relevant pieces of code with the implemented solution from my workbook project: ```gdscript extends Control #... # The signal below will be emitted whenever # fucntions 'advance()' or 'go_back()' are called. signal current_item_index_changed # **DEFINING THE CUSTOM SIGNAL** # This signal is then connected to 'timer.stop()' # in the 'show_text()' function. More on that below. func _ready() -> void: show_text() next_button.pressed.connect(advance) back_button.pressed.connect(go_back) # This function is called when 'next_button' is pressed. func advance() -> void: current_item_index += 1 current_item_index_changed.emit() # **EMITTING THE CUSTOM SIGNAL** if current_item_index == dialogue_items.size(): current_item_index = 0 show_text() else: show_text() # This function is called when 'back_button' is pressed. func go_back() -> void: if current_item_index != 0: current_item_index_changed.emit() # **EMITTING THE CUSTOM SIGNAL** current_item_index -= 1 show_text() func show_text() -> void: # ... # Animating text. rich_text_label.visible_ratio = 0.0 var text_appearing_duration: float = log(rich_text_label.get_total_character_count()) / 2.0 var tween := create_tween() tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration).from_current() # Play audio while text animates. var sound_max_length: float = audio_stream_player.stream.get_length() - text_appearing_duration var sound_start_position: float = randf() * sound_max_length audio_stream_player.play(sound_start_position) # ** THIS IS WHERE THE TIMER IS CREATED AND UTILIZED (to stop aduio playback) ** var timer := Timer.new() current_item_index_changed.connect(timer.stop) # <- This will stop the old timer before starting... add_child(timer) # ...a new one when player advances too quickly. timer.wait_time = text_appearing_duration timer.one_shot = true timer.start() timer.timeout.connect(audio_stream_player.stop) # The timer will still stop the audio playback if the player waits for the text animation to finish. # Body slide animation. slide_in() ## Animate the body sliding in upon advancing/rewinding dialogue. func slide_in() -> void: # ... ``` I also more than welcome any criticism or possible problems this could create. Or perhaps a better solution. 2 0 Aug. 25, 2024
  • Resizing the window during an animation?big-hearted-emu```gdscript extends Control @onready var black_bar_top: ColorRect = %BlackBarTop @onready var black_bar_bottom: ColorRect = %BlackBarBottom @onready var button: Button = %Button func _ready() -> void: button.pressed.connect(_on_button_pressed) func _on_button_pressed() -> void: animate_black_bars() # Position of bottom bar breaks if window resized during animation. func animate_black_bars() -> void: var black_bar_top_size := 50.0 var black_bar_bottom_position := black_bar_bottom.position.y - 100.0 var black_bar_animation_time := 2.8 var tween := create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT) tween.set_parallel() tween.tween_property(black_bar_top, "size:y", black_bar_top_size, black_bar_animation_time) tween.tween_property(black_bar_bottom, "position:y", black_bar_bottom_position, black_bar_animation_time) tween.tween_property(button, "modulate:a", 0.0, black_bar_animation_time) tween.finished.connect(func() -> void: button.queue_free() ) ``` [https://i.imgur.com/PX5Inng.gif](https://i.imgur.com/PX5Inng.gif) The unwanted effect can be seen in the gif. The problem is how I animate the bottom bar. The black bars are anchored **top wide** and **bottom wide** respectively. Default **size.x** based on *screen size* and default **size.y** set to **zero**. I initially tried to animate them by changing both of their size.y properties, except I cannot make the bottom bar's size.y negative. Second attempt was rotating the bottom bar 180° and positioning it manually back to its place. The problem then is that when resizing the screen, it extends in the opposite direction. Third attempt was simply having the bar already visible off screen and animating its **position.y** to the correct place. This is the code above. The problem I'm stuck on now is that I have added `var black_bar_bottom_position := black_bar_bottom.position.y - 100.0` in order to prevent the bar from doing weird stuff based on your window size at the time of pressing the button. Once you do press the button, however, if you resize the window during the animation, the bar's position gets out of whack. If you resize it after the animation finished, everything is fine. Now, I get how and why everything happens, I just have no idea how to fix it. The only thing I can think of is checking every frame for the current screen size and adjusting the position of the bar based on that, which... there has to be a better way of doing this. Preventing the resizing of the window from the project settings is another one, but it does mention it's not reliable. 8 0 Aug. 12, 2024
  • tween.TRANS_QUART vs. tween.set_trans(Tween.TRANS_QUART)RussellApologies if I just missed this in the lesson, but what is the difference between tween.TRANS_QUART and tween.set_trans(Tween.TRANS_QUART)? In this example, at least, the result seemed to be the same. BTW, you guys have done a great job with this entire course so far. I'm really enjoying this (and learning) a lot! 1 0 Jul. 27, 2024
  • Auto-continuing the dialoguesdyldoHi, Regarding the slideshow challenge (and using your current code), is there a way to auto-continue the dialogue by calling func advance() after a certain delay? For example: after x amount of seconds, the dialogue will "click next" automatically and advance to the next dialogue. I tried using set_method(advance, from_value, to_value, duration) and adding an idx parameter to func advance() so the current_item_idx = idx; however, that just seems to break the code. ```gdscript func show_text() -> void: var current_item := dialogue_items[current_item_idx] rich_text_label.text = current_item["text"] expression.texture = current_item["expression"] rich_text_label.visible_ratio = 0.0 var text_speed: float = rich_text_label.text.length() / 30.0 var tween := create_tween().set_parallel() tween.tween_method(advance, 0, 3, 30.0).set_delay(5.0) tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_speed) var sound_length := audio_stream_player.stream.get_length() - text_speed audio_stream_player.play(randf() * sound_length) tween.finished.connect(audio_stream_player.stop) slide_in() func advance(idx: int) -> void: current_item_idx = idx if current_item_idx == dialogue_items.size(): current_item_idx = 0 show_text() ``` Can you explain why this doesn't work? Can you also explain more behind how the set_method works? 1 0 Jul. 25, 2024
  • Expression remains a bit transparentandrea-putortiHey there! I did everything as mentioned in the lesson and my code does look like the same as yours, but when I start the scene, the Expression remains a bit transparent, at the end of the animation, although I changed the modulate of the whole Body (Expression is indeed its child). This is my project: [https://we.tl/t-YJUxliAZy4](https://we.tl/t-YJUxliAZy4) Thanks! Here's the code too: ```gdscript func slide_in() -> void: var tween := create_tween() tween.set_trans(Tween.TRANS_QUART) tween.set_ease(Tween.EASE_OUT) body.position.x = 200.0 tween.tween_property(body, "position:x", 0.0, 0.3) body.modulate.a = 0.0 tween.parallel().tween_property(body, "modulate:a", 1.0, 0.2) ``` 2 0 Jul. 01, 2024
  • Null value error using TimerStonemonkeyI get the error " cannot call method 'set delay' on a null value" on the second `await` method call. I'm just trying to use a timer to wait for the previous tweens to finish and also hope that the new `texture` value get's applied before animating the second set of tweens. ```gdscript func play_slides()->void: var tween = create_tween() slide.texture = slides[0] tween.tween_property(slide, "modulate:a", 1.0, 2.0) tween.tween_property(slide, "modulate:a", 0.0, 2.0).set_delay(2.0) timer.wait_time = 2.0 timer.start() await timer.timeout slide.texture = slides[1] tween.tween_property(slide, "modulate:a", 1.0, 2.0) tween.tween_property(slide, "modulate:a", 0.0, 2.0).set_delay(2.0) #cannot call method `set delay` on a null value ``` 5 0 Jun. 14, 2024
  • Quiz sctructureArtemaleIn case I am creating a quiz-type game where I should choose the right answer button. What is an optimal way for 30 questions quiz? To create 30 scenes with 1 question per scene or should I try to use arrays, dictionaries etc. to stay with one main scene? 9 0 Jun. 10, 2024
  • Question about set_parallel() vs tween.parallel()fledgerI tried to tackle the first scale challenge before going forward with the slideshow challenge. When I messed with the scale it seemed to mess with the animations all playing. I removed the tween.parallel from the tweens and placed set_parallel()higher up in the code. However, this seems to ignore the position animation entirely. I will share the code as comments because I am having issues making it all in one post. My apologies in advance! 4 0 Jun. 02, 2024
  • Scale/Slideshow challenge results!pikminfan71Hope you like how it turned out! [https://youtu.be/s_6bjRCZHZg](https://youtu.be/s_6bjRCZHZg) 4 0 May. 31, 2024
Site is in BETA!found a bug?