From 06184c13af639917e1af5a1bc298b0022c1398ea Mon Sep 17 00:00:00 2001
From: David Luevano Alvarado
The source code can be found at luevano/flappybirdgodot (main
branch). Playable at itch.io:
Disclaimer: I started the port back in Godot 4.0 something and left the project for a while, then opened the project again in Godot 4.1, and it didn’t ask to convert anything so probably nowadays the conversion is better.
+These include the first changes for fixing some of the conflicting code to at least make it run (no gameplay) as well as project settings adjustments.
+Texture
to a Texture2D
). The important parameter to change is the Filter for the textures.Main.tscn
) will suffice: Go to Inspector -> CanvasItem -> Texture and change Filter from “Inherit” to “Nearest”.SavedData
singleton, change from File
to ConfigFile
and refactor. This is really not needed for making it run, but I changed this right away.velocity
property of Player
which is already included in CharacterBody2D
.TileMap
related code as tile maps have changed drastically, they need more effort to covnert.String(int)
to str(int)
.Now that the game at least runs, next thing is to make it “playable”:
+AnimatedSprite
changed to AnimatedSprite2D
(with the inclusion of AnimatedSprite3D
). This node type changed with the automatic conversion.playing
property, the method is_playing()
needs to be used.default_gravity
from the ProjectSettings
no longer needs to be multiplied by 10
to have reasonable numbers. The default is now 980
instead of 98
. I later changed this when refactoring the code and fine-tuning the feel of the movement.set_collision_mask_value
(and similar with the layer). Before, the mask/layer was specified by the bit
which started from 0
, but now it is accessed by the layer_number
which starts from 1
.This is the most challenging part as the TileMap
system changed drastically, it is basically a from the ground up redesign, luckily the TileMap
s I use are really simple. Since this is not intuitive from the get-go, I took some notes on the steps I took to set up the world TileMap
.
Instead of using one scene per TileMap
only one TileMap
can be used with multiple Atlas in the TileSet
. Multiple physics layers can now be used per TileSet
so you can separate the physics collisions on a per Atlas or Tile basis. The inclusion of Tile patterns also helps when working with multiple Tiles for a single cell “placement”. How I did it:
TileMap
node, called WorldTileMap.tscn
, with only one TileSet
as multiple Atlas‘ can be used (this would be a single TileSet
in Godot 3).TileSet
, select the WorldTileMap
and go to Inspector -> TileMap -> TileSet then click on “TileSet
, it needs to be selected, either by clicking in the Inspector section or on the bottom of the screen (by default) to the left of TileMap, as shown in the image below.TileSet
(one for the ground tiles and another for the pipes) by clicking on the “Add” button (as shown in the image above) and then on “Atlas”.ground
and add the texture atlas (the spritesheet) by dragging and dropping in the “0
and increment for each atlas, but if they’re not 0
and 1
change them.1x2
tiles. This can be acomplished by removing all tiles except for one, then going to the “Select” section of the atlas, selecting a tile and extending it either graphically by using the yellow circles or by using the properties, as shown in the image below.WorldTileMap
‘s TileSet
and clicking on “Add Element” at the TileMap -> TileSet -> Physics Layer twice, one physics layer per atlas. Then set the collision’s layers and masks accordingly (ground on layer 2, pipe on 3). In my case, based on my already set layers.- Notice that the polygon is drawn in *Physics Layer 0*. Using the grid option to either `1` or `2` is useful when drawing the polygon, make sure the polygon closes itself or it wont be drawn.
+
+0
.Basically merged all 3 scripts (ground_tile_map.gd
, pipe_tile_map.gd
and world_tiles.gd
) into one (world_tile_map.gd
) and immediatly was able to delete a lot of signal calls between those 3 scripts and redundant code.
The biggest change in the scripting side are the functions to place tiles. For Godot 3:
+# place single tile in specific cell
+void set_cell(x: int, y: int, tile: int, flip_x: bool = false, flip_y: bool = false, transpose: bool = false, autotile_coord: Vector2 = Vector2( 0, 0 ))
+void set_cellv(position: Vector2, tile: int, flip_x: bool = false, flip_y: bool = false, transpose: bool = false, autotile_coord: Vector2 = Vector2( 0, 0 ))
+
+Whereas in Godot 4:
+# place single tile in specific cell
+void set_cell(layer: int, coords: Vector2i, source_id: int = -1, atlas_coords: Vector2i = Vector2i(-1, -1), alternative_tile: int = 0)
+# erase tile at specific cell
+void erase_cell(layer: int, coords: Vector2i)
+
+How to use these functions in Godot 4 (new properties or differences/changes):
+layer
: for my case I only use 1 layer so it is always set to 0
.coords
: would be the equivalent to position
for set_cellv
in Godot 3.source_id
: which atlas to use (ground: 0
or pipe 1
).atlas_coords
: tile to use in the atlas. This would be the equivalent to tile
in Godot 3.alternative_tile
: for tiles that have alternatives such as mirrored or rotated tiles, not required in my case.Setting source_id=-1
, atlas_coords=Vector21(-1,-1)
or alternative_tile=-1
will delete the tile at coords
, similar to just using erase_cell
.
With the addition to Tile patterns (to place multiple tiles), there is a new function:
+# place pattern
+void set_pattern(layer: int, position: Vector2i, pattern: TileMapPattern)
+
+Where position
has the same meaning as coords
in set_cell
/erase_cell
, not sure why it has a different name. The pattern
can be obtained by using get_pattern
method on the tile_set
property of the TileMap
. Something like:
var pattern: TileMapPattern = tile_set.get_pattern(index)
+
+Other than that, Vector2
needs to be changed to Vector2i
.
General changes and additions that have nothing to do with porting to Godot 4, things I wanted to add regardless of the version.
+The audio in the Godot 3 version was added in the last minute and it was blasting by default with no option to decrease the volume or mute it. To deal with this:
+The basic code required for these features is the following:
+# get audio bus index
+var audio_bus_name: String = "Master"
+var _bus: int = AudioServer.get_bus_index(audio_bus_name)
+
+# change the volume
+var linear_volume: float = 0.5 # 50%, needs to be between 0.0 and 1.0
+var db_volume: float = linear_to_db(linear_volume)
+AudioServer.set_bus_volume_db(_bus, db_volume)
+
+# mute
+AudioServer.set_bus_mute(_bus, true) # false to unmute
+
+Just be careful with how the linear_volume
is set (from a button or slider) as it has to be between 0.0
and 1.0
.
Moved all the signal logic into an event bus to get rid of the coupling I had. This is accomplished by:
+event.gd
and can be accessed with Event
.event.gd
.Event.<signal_name>.emit(<optional_args>)
.Event.<signal_name>.connect(<callable>[.bind(<optional_args>)])
bind
, only extras are needed here.Really the only UI I had before was for rendering fonts, and the way fonts work changed a bit. Before, 3 resources were needed as noted in my previous entry:
+.ttf
for example).DynamicFontData
: used to point to a font file (.ttf
) and then used as base resource.DynamicFont
: usable in godot control nodes which holds the DynamicFontData
and configuration such as size.Now only 1 resource is needed: FontFile
which is the .ttf
file itself or a godot-created resource. There is also a FontVariation
option, which takes a FontFile
and looks like its used to create fallback options for fonts. The configuration (such as size) is no longer held in the font resource, but rather in the parent control node (like a Label
). Double clicking on the .ttf
file and disabling antialiasing and compression is something that might be needed. Optionally create a FontLabelSettings
which will hold the .ttf
file and used as base for Label
s. Use “Make Unique” for different sizes. Another option is to use Themes and Variations.
I also created the respective volume button and slider UI for the added audio functionality as well as creating a base Label
to avoid repeating configuration on each Label
node.
Small changes that don’t affect much:
+@export
to @export_range
. The auto conversion didn’t use the correct annotation and instead used a comment.game_scale
methodolgy as it was inconsistent. Now only one size is used as base and everything else is just scaled with the root
Window
.The game was originally developed with Godot 4.0 alpha 8, but it didn’t support HTML5 (webassembly) export… so I backported to Godot 3.5 rc1.
+Note: I’ve updated the game to Godot 4 and documented it on my FlappyBird devlog 2 entry.
Not going to specify all the details, only the needed parts and what could be confusing, as the source code is available and can be inspected; also this assumes minimal knowledge of Godot in general. Usually when I mention that a set/change of something it usually it’s a property and it can be found under the Inspector on the relevant node, unless stated otherwise; also, all scripts attached have the same name as the scenes, but in snake_case (scenes/nodes in PascalCase).
One thing to note, is that I started writing this when I finished the game, so it’s hard to go part by part, and it will be hard to test individual parts when going through this as everything is depending on each other. For the next devlog, I’ll do it as I go and it will include all the changes to the nodes/scripts as I was finding them, probably better idea and easier to follow.
-The source code can be found at luevano/flappybird_godot#godot-3.5, it also contains the exported versions for HTML5, Windows and Linux (be aware that the sound might be too high and I’m too lazy to make it configurable, it was the last thing I added), or you could also go to the itch.io page I setup where it’s playable in the browser:
+The source code can be found at luevano/flappybirdgodot#godot-3.5 (godot-3.5
branch), it also contains the exported versions for HTML5, Windows and Linux (be aware that the sound might be too high and I’m too lazy to make it configurable, it was the last thing I added on the latest version this is fixed and audio level is configurable now). Playable on itch.io (Godot 4 version):