From ab5cc111d51a8975f5f82e5a0b497b5f752acc5d Mon Sep 17 00:00:00 2001 From: David Luevano Alvarado Date: Fri, 1 Mar 2024 04:13:22 -0600 Subject: add final flappybird godot devlog entry --- live/blog/rss.xml | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) (limited to 'live/blog/rss.xml') diff --git a/live/blog/rss.xml b/live/blog/rss.xml index c725869..775006f 100644 --- a/live/blog/rss.xml +++ b/live/blog/rss.xml @@ -22,6 +22,157 @@ Luévano's Blog https://blog.luevano.xyz + + Final improvements to the FlappyBird clone and Android support devlog 3 + https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html + https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html + Fri, 01 Mar 2024 10:00:57 GMT + English + Gamedev + Gdscript + Godot + Notes on the final improvements to my FlappyBird clone made in Godot 4.x. Also details on the support for Android. + Decided to conclude my FlappyBird journey with one last set of improvements, following up on devlogs 1 and 2. Focusing on refactoring, better UI, sprite selection and Android support.

+

I missed some features that I really wanted to get in but I’m already tired of working on this toy project and already eager to move to another one. Most of the features I wanted to add are just QoL UI enhancements and extra buttons basically.

+

The source code can be found at luevano/flappybirdgodot. Playable at itch.io:

+

+ +

Table of contents

+ +

Refactoring

+

The first part for my refactor was to move everything out of the src/ directory into the root directory of the git repository, organizing it a tiny bit better, personal preference from what I’ve learned so far. I also decided to place all the raw aseprite assets next to the imported one, this way its easier to make modifications and then save directly in the same directory. Also, a list of other refactoring done:

+
    +
  • The way I handled the gameplay means that I needed to make the camera, background and the (ceiling and tiles) “detectors” move along with the player, while restricting their movement in the x axis, really hacky. Instead, I did what I should’ve done from the beginning… just let the tiles move backwards and keep everything static with the player only moving up an down (as how I stated at the beginning of FlappyBirdgodot devlog 1 but didn’t actually follow).
  • +
  • Moved the set_process methodology to their own scripts, instead of handling everything in main.gd while also taking advantage of how signals work now. Instead of doing:
  • +
+
func _ready():
+    Event.game_pause.connect(_on_game_pause)
+
+func _on_game_pause(pause: bool):
+    set_process(pause)
+
+

Just connecting to set_process is enough:

+
func _ready():
+    Event.game_pause.connect(set_process)
+    # and when the signal doesn't send anything:
+    Event.game_start.connect(set_process.bind(true))
+    Event.game_over.connect(set_process.bind(false))
+
+

Improvements

+

Background parallax

+

First thing was to add a moving background functionality, by adding 2 of the same Sprite2D‘s one after another and everytime the first sprite moves out of the screen, position it right after the second sprite. Some sample code to accomplish this:

+
func _ready():
+   # Sprite2D and CompressedTexture2D nodes
+   background_orig.texture = background_texture
+   texture_size = background_orig.texture.get_size()
+
+   backgrounds.append(background_orig.duplicate())
+   backgrounds.append(background_orig.duplicate())
+   backgrounds[1].position = background_orig.position + Vector2(texture_size.x, 0.0)
+
+   add_child(backgrounds[0])
+   add_child(backgrounds[1])
+   background_orig.visible = false
+
+# ifirst (index first) it's a boolean value starting with false and
+#   its a hacky way of tracking the first sprites
+#   (the one closest to the left of the screen) in the array
+func _process(delta: float):
+    for background in backgrounds:
+        background.move_local_x(- SPEED * delta)
+
+    # moves the sprite that just exited the screen to the right of the upcoming sprite
+    if backgrounds[int(ifirst)].position.x <= - background_orig.position.x:
+        backgrounds[int(ifirst)].position.x = backgrounds[int(!ifirst)].position.x + texture_size.x
+        ifirst = !ifirst
+
+

Then I added background parallax by separating the background sprites in two: background and “foreground” (the buildings in the original sprites). And to move them separately just applied the same logic described above with 2 different speeds.

+

Sprite switcher

+

Also added a way to select between the bird sprites and the backgrounds, currently pretty primitive but functional. Accomplished this by holding textures in an exported array, then added a bit of logic to cycle between them (example for the background):

+
func _get_new_sprite_index(index: int) -> int:
+    return clampi(index, 0, background_textures.size() - 1)
+
+
+func _set_sprites_index(index: int) -> int:
+    var new_index: int = _get_new_sprite_index(index)
+    if new_index == itexture:
+        return new_index
+    for bg in backgrounds:
+        bg.texture = background_textures[new_index]
+    for fg in foregrounds:
+        fg.texture = foreground_textures[new_index]
+    itexture = new_index
+    return new_index
+
+

Then, in custom signals I just call _set_sprites_index with a texture_index +/- 1.

+

Save data

+

Moved from manual ConfigFile (which is an .ini file basically) to Resource which is easier to work with and faster to implement.

+

Accomplished by defining a new data_resource.gd:

+
class_name DataResource
+extends Resource
+
+@export var high_score: int
+@export var volume: float
+@export var mute: bool
+@export var bird: int
+@export var background: int
+
+func _init():
+    high_score = 0
+    volume = 0.5
+    mute = false
+    bird = 0
+    background = 0
+
+

Where the @exports are not needed unless you need to manage the .tres resource files for testing in-editor.

+

Then, the data.gd script needs to be changed accordingly, most notably:

+
    +
  • The file extension is .tres instead of .cfg.
  • +
  • No need for “config sections”.
  • +
  • Saving changes to:
  • +
+
func save():
+    var err: int = ResourceSaver.save(_data, DATA_PATH)
+    if err != OK:
+        print("[ERROR] Couldn't save data.")
+
+
    +
  • The loader gets simplified (mostly because the default values are set in the _init() function of the data_resource.gd) to:
  • +
+
func _load_data():
+    if ResourceLoader.exists(DATA_PATH):
+        _data = load(DATA_PATH)
+    else:
+        _data = DataResource.new()
+        save()
+
+
    +
  • The attributes/config/saved data can be retrieved directly by the data_resource.gd variable name, for example: instead of _data.get_value(SCORE_SECTION, "high_score") it’s now simply _data.high_score. And similar for setting the values.
  • +
+

Compared to the 3.x version it is a lot more simple. Though I still have setters and getters for each attribute/config (I’ll se how to change this in the future).

+

Android

+

I did add android support but it’s been so long since I did it that I actually don’t remember (this entry has been sitting in a draft for months). In general I followed the official guide for Exporting for Android, setting up Android studio and remotely debugging with my personal phone; it does take a while to setup but after that it’s as simple as doing “one click deploys”.

+

Most notably, I had to enable touch screen support and make the buttons clickable either by an actual mouse click or touch input. Some of the Project Settings that I remember that needs changes are:

+
    +
  • display/window/handheld/orientation set to Portrait.
  • +
  • input_devices/pointing/emulate_touch_from_mouse and input_devices/pointing/emulate_mouse_from_touch both set to on.
  • +
+

Misc

+

Found a bug on the ScoreDetector where it would collide with the Ceiling. While this is really not a problem outside of me doing tests I fixed it by applying the correct layer/mask.

]]>
+
Godot layers and masks notes https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html -- cgit v1.2.3-70-g09d2