summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Luevano Alvarado <david@luevano.xyz>2024-03-01 04:13:22 -0600
committerDavid Luevano Alvarado <david@luevano.xyz>2024-03-01 04:13:22 -0600
commitab5cc111d51a8975f5f82e5a0b497b5f752acc5d (patch)
treea722ea0636b4d35b9c47120b877de32e96fdfc74
parente6df5eecddf5443824aba28d09563df8c725debd (diff)
add final flappybird godot devlog entry
-rw-r--r--db/db_blog.psv1
-rw-r--r--live/blog/g/flappybird_godot_devlog_3.html292
-rw-r--r--live/blog/g/godot_layers_and_masks_notes.html6
-rw-r--r--live/blog/index.html4
-rw-r--r--live/blog/rss.xml151
-rw-r--r--live/blog/sitemap.xml6
-rw-r--r--live/blog/tag/@english.html4
-rw-r--r--live/blog/tag/@gamedev.html4
-rw-r--r--live/blog/tag/@gdscript.html4
-rw-r--r--live/blog/tag/@godot.html4
-rw-r--r--src/blog/g/flappybird_godot_devlog_3.md170
11 files changed, 641 insertions, 5 deletions
diff --git a/db/db_blog.psv b/db/db_blog.psv
index 562d56c..62fda81 100644
--- a/db/db_blog.psv
+++ b/db/db_blog.psv
@@ -29,3 +29,4 @@ a/jellyfin_server_with_sonarr_radarr.md|1690173014.2185652|1690173137.3785434|83
a/pastebin_alt_with_privatebin.md|1692524793.86829|1692527331.9501805|7605e02fc2b613617be56fdf43471664|code,english,server,tools,tutorial
g/flappybird_godot_devlog_2.md|1693178890.8924305|1693582149.5222216|ba06223cc0870f39ef7890f154473cfe|english,gamedev,gdscript,godot
g/godot_layers_and_masks_notes.md|1693303806.3574584|0.0|407b8f66cdde17ac2f8c113921805d98|english,gamedev,gdscript,godot
+g/flappybird_godot_devlog_3.md|1709287257.5775516|1709287880.287344|47f999654b2cc94b7a780888b92e16d7|english,gamedev,gdscript,godot
diff --git a/live/blog/g/flappybird_godot_devlog_3.html b/live/blog/g/flappybird_godot_devlog_3.html
new file mode 100644
index 0000000..953eee4
--- /dev/null
+++ b/live/blog/g/flappybird_godot_devlog_3.html
@@ -0,0 +1,292 @@
+<!DOCTYPE html>
+<html class="theme-dark" lang="en
+"
+ prefix="og: https://ogp.me/ns#">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" href="https://static.luevano.xyz/images/icons/favicon.ico">
+<title>Final improvements to the FlappyBird clone and Android support devlog 3 -- Luévano's Blog</title>
+ <meta name="description" content="Notes on the final improvements to my FlappyBird clone made in Godot 4.x. Also details on the support for Android."/>
+<link rel="alternate" type="application/rss+xml" href="https://blog.luevano.xyz/rss.xml" title="Luévano's Blog RSS">
+ <!-- general style -->
+ <link rel="stylesheet" type="text/css" href="https://static.luevano.xyz/css/style.css">
+ <link rel="stylesheet" type="text/css" href="https://static.luevano.xyz/fork-awesome/css/fork-awesome.min.css">
+ <link rel="stylesheet" type="text/css" href="https://static.luevano.xyz/font-awesome/css/all.min.css">
+ <!-- theme related -->
+ <script type="text/javascript" src="https://static.luevano.xyz/scripts/theme.js"></script>
+ <link id="theme-css" rel="stylesheet" type="text/css" href="https://static.luevano.xyz/css/theme.css">
+ <!-- misc functions-->
+ <script type="text/javascript" src="https://static.luevano.xyz/scripts/return_top.js"></script>
+ <!-- extra -->
+
+
+ <!-- highlight support for code blocks -->
+<script type="text/javascript" src="https://static.luevano.xyz/hl/highlight.min.js"></script>
+<script type="text/javascript">
+ hljs.initHighlightingOnLoad();
+</script>
+<link id="code-theme-css" rel="stylesheet" type="text/css" href="https://static.luevano.xyz/hl/styles/nord.min.css">
+
+
+ <!-- Specific to GDScript -->
+ <script type="text/javascript" src="https://static.luevano.xyz/hl/languages/gdscript.min.js"></script>
+
+ <!-- og meta -->
+ <meta property="og:title" content="Final improvements to the FlappyBird clone and Android support devlog 3 -- Luévano's Blog"/>
+ <meta property="og:type" content="article"/>
+ <meta property="og:url" content="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.md"/>
+ <meta property="og:image" content="https://static.luevano.xyz/images/b/default.png"/>
+ <meta property="og:description" content="Notes on the final improvements to my FlappyBird clone made in Godot 4.x. Also details on the support for Android."/>
+ <meta property="og:locale" content="en"/>
+ <meta property="og:site_name" content="Luévano's Blog"/>
+ </head>
+
+ <body>
+ <header>
+<nav>
+ <ul>
+ <li>
+ <a href="https://luevano.xyz/"><i class="fas fa-home" alt="Home"></i><span>Home</span></a>
+ </li>
+
+ <li>
+ <a href="https://blog.luevano.xyz/"><i class="fas fa-book-open" alt="Blog"></i><span>Blog</span></a>
+ </li>
+
+ <li>
+ <a href="https://art.luevano.xyz/"><i class="fas fa-paint-brush" alt="Art"></i><span>Art</span></a>
+ </li>
+
+ <li><i class="fab fa-git" alt="Git"></i><span>Git</span>
+ <ul>
+ <li><a href="https://git.luevano.xyz/" target="_blank"><i class="fab fa-git-alt" alt="Git-alt"></i></a></li>
+
+ <li><a href="https://github.com/luevano" target="_blank"><i class="fab fa-github" alt="Github"></i></a></li>
+
+ <li><a href="https://gitlab.com/dluevano" target="_blank"><i class="fab fa-gitlab" alt="Gitlab"></i></a></li>
+ </ul>
+ </li>
+
+ <li><i class="fas fa-box-open" alt="Stuff"></i><span>Stuff</span>
+ <ul>
+ <li><a href="https://gb.luevano.xyz/"><i class="fas fa-gamepad" alt="Gameboy"></i><span>Gameboy</span></a></li>
+ </ul>
+ </li>
+ </ul>
+</nav>
+
+<button class="theme-switcher" onclick="toggleTheme()"><i class="fas fa-moon"></i><i class="fas fa-sun"></i></button>
+
+ </header>
+
+ <main>
+ <div class="return-top">
+ <button class="return-top" onclick="returnTop()" id="returnTopButton">
+ <i class="fas fa-arrow-up" alt="Return to top"></i>
+ </button>
+ </div>
+ <h1>Final improvements to the FlappyBird clone and Android support devlog 3</h1>
+
+ <p>Decided to conclude my FlappyBird journey with one last set of improvements, following up on devlogs <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html">1</a> and <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">2</a>. Focusing on <strong>refactoring</strong>, <strong>better UI</strong>, <strong>sprite selection</strong> and Android support.</p>
+<p>I missed some features that I really wanted to get in but I&rsquo;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 <em>QoL</em> UI enhancements and extra buttons basically.</p>
+<p>The source code can be found at <a href="https://github.com/luevano/flappybirdgodot">luevano/flappybirdgodot</a>. Playable at <a href="https://lorentzeus.itch.io/flappybirdgodot">itch.io</a>:</p>
+<p style="text-align:center"><iframe src="https://itch.io/embed/1551015?dark=true" width="208" height="167" frameborder="0"><a href="https://lorentzeus.itch.io/flappybirdgodot">FlappyBirdGodot by Lorentzeus</a></iframe></p>
+
+<h2 id="table-of-contents">Table of contents<a class="headerlink" href="#table-of-contents" title="Permanent link">&para;</a></h2>
+<div class="toc">
+<ul>
+<li><a href="#table-of-contents">Table of contents</a></li>
+<li><a href="#refactoring">Refactoring</a></li>
+<li><a href="#improvements">Improvements</a><ul>
+<li><a href="#background-parallax">Background parallax</a></li>
+<li><a href="#sprite-switcher">Sprite switcher</a></li>
+<li><a href="#save-data">Save data</a></li>
+</ul>
+</li>
+<li><a href="#android">Android</a></li>
+<li><a href="#misc">Misc</a></li>
+</ul>
+</div>
+<h2 id="refactoring">Refactoring<a class="headerlink" href="#refactoring" title="Permanent link">&para;</a></h2>
+<p>The first part for my refactor was to move everything out of the <code>src/</code> directory into the root directory of the git repository, organizing it a tiny bit better, personal preference from what I&rsquo;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:</p>
+<ul>
+<li>The way I handled the gameplay means that I needed to make the camera, background and the (ceiling and tiles) &ldquo;detectors&rdquo; move along with the player, while restricting their movement in the <code>x</code> axis, really hacky. Instead, I did what I should&rsquo;ve done from the beginning&hellip; 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 <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html">FlappyBirdgodot devlog 1</a> but didn&rsquo;t actually follow).</li>
+<li>Moved the <code>set_process</code> methodology to their own scripts, instead of handling everything in <code>main.gd</code> while also taking advantage of how signals work now. Instead of doing:</li>
+</ul>
+<pre><code class="language-gdscript">func _ready():
+ Event.game_pause.connect(_on_game_pause)
+
+func _on_game_pause(pause: bool):
+ set_process(pause)
+</code></pre>
+<p>Just connecting to <code>set_process</code> is enough:</p>
+<pre><code class="language-gdscript">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))
+</code></pre>
+<h2 id="improvements">Improvements<a class="headerlink" href="#improvements" title="Permanent link">&para;</a></h2>
+<h3 id="background-parallax">Background parallax<a class="headerlink" href="#background-parallax" title="Permanent link">&para;</a></h3>
+<p>First thing was to add a moving background functionality, by adding 2 of the same <code>Sprite2D</code>&lsquo;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:</p>
+<pre><code class="language-gdscript">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 &lt;= - background_orig.position.x:
+ backgrounds[int(ifirst)].position.x = backgrounds[int(!ifirst)].position.x + texture_size.x
+ ifirst = !ifirst
+</code></pre>
+<p>Then I added background parallax by separating the background sprites in two: background and &ldquo;foreground&rdquo; (the buildings in the original sprites). And to move them separately just applied the same logic described above with 2 different speeds.</p>
+<h3 id="sprite-switcher">Sprite switcher<a class="headerlink" href="#sprite-switcher" title="Permanent link">&para;</a></h3>
+<p>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):</p>
+<pre><code class="language-gdscript">func _get_new_sprite_index(index: int) -&gt; int:
+ return clampi(index, 0, background_textures.size() - 1)
+
+
+func _set_sprites_index(index: int) -&gt; 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
+</code></pre>
+<p>Then, in custom signals I just call <code>_set_sprites_index</code> with a <code>texture_index +/- 1</code>.</p>
+<h3 id="save-data">Save data<a class="headerlink" href="#save-data" title="Permanent link">&para;</a></h3>
+<p>Moved from manual <code>ConfigFile</code> (which is an <code>.ini</code> file basically) to <code>Resource</code> which is easier to work with and faster to implement.</p>
+<p>Accomplished by defining a new <code>data_resource.gd</code>:</p>
+<pre><code class="language-gdscript">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
+</code></pre>
+<p>Where the <code>@export</code>s are not needed unless you need to manage the <code>.tres</code> resource files for testing in-editor.</p>
+<p>Then, the <code>data.gd</code> script needs to be changed accordingly, most notably:</p>
+<ul>
+<li>The file extension is <code>.tres</code> instead of <code>.cfg</code>.</li>
+<li>No need for &ldquo;config sections&rdquo;.</li>
+<li>Saving changes to:</li>
+</ul>
+<pre><code class="language-gdscript">func save():
+ var err: int = ResourceSaver.save(_data, DATA_PATH)
+ if err != OK:
+ print(&quot;[ERROR] Couldn't save data.&quot;)
+</code></pre>
+<ul>
+<li>The loader gets simplified (mostly because the default values are set in the <code>_init()</code> function of the <code>data_resource.gd</code>) to:</li>
+</ul>
+<pre><code class="language-gdscript">func _load_data():
+ if ResourceLoader.exists(DATA_PATH):
+ _data = load(DATA_PATH)
+ else:
+ _data = DataResource.new()
+ save()
+</code></pre>
+<ul>
+<li>The attributes/config/saved data can be retrieved directly by the <code>data_resource.gd</code> variable name, for example: instead of <code>_data.get_value(SCORE_SECTION, "high_score")</code> it&rsquo;s now simply <code>_data.high_score</code>. And similar for setting the values.</li>
+</ul>
+<p>Compared to the <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html#saved-data">3.x version</a> it is a lot more simple. Though I still have setters and getters for each attribute/config (I&rsquo;ll se how to change this in the future).</p>
+<h2 id="android">Android<a class="headerlink" href="#android" title="Permanent link">&para;</a></h2>
+<p>I did add android support but it&rsquo;s been so long since I did it that I actually don&rsquo;t remember (this entry has been sitting in a draft for months). In general I followed the official guide for <a href="https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_android.html">Exporting for Android</a>, setting up Android studio and remotely debugging with my personal phone; it does take a while to setup but after that it&rsquo;s as simple as doing <em>&ldquo;one click deploys&rdquo;</em>.</p>
+<p>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 <em>Project Settings</em> that I remember that needs changes are:</p>
+<ul>
+<li><em>display/window/handheld/orientation</em> set to <code>Portrait</code>.</li>
+<li><em>input_devices/pointing/emulate_touch_from_mouse</em> and <em>input_devices/pointing/emulate_mouse_from_touch</em> both set to <code>on</code>.</li>
+</ul>
+<h2 id="misc">Misc<a class="headerlink" href="#misc" title="Permanent link">&para;</a></h2>
+<p>Found a bug on the <code>ScoreDetector</code> where it would collide with the <code>Ceiling</code>. While this is really not a problem outside of me doing tests I fixed it by <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">applying the correct layer/mask</a>.</p>
+
+ <div class="page-nav">
+
+ <span class="index">
+ <a href="https://blog.luevano.xyz" alt="Index">
+ <i class="fas fa-home" alt="Home"></i>
+ <span>Index</span>
+ </a>
+ </span>
+
+ <span class="previous">
+ <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html" alt="Previous">
+ <i class="fas fa-arrow-right" alt="Arrow right"></i>
+ <span>Previous</span>
+ </a>
+ </span>
+</div>
+
+
+ <hr>
+ <div class="article-info">
+ <p>By David Luévano</p>
+ <p>Created: Fri, Mar 01, 2024 @ 10:00 UTC</p>
+ <p>Modified: Fri, Mar 01, 2024 @ 10:11 UTC</p>
+ <div class="article-tags">
+ <p>Tags:
+<a href="https://blog.luevano.xyz/tag/@english.html">english</a>, <a href="https://blog.luevano.xyz/tag/@gamedev.html">gamedev</a>, <a href="https://blog.luevano.xyz/tag/@gdscript.html">gdscript</a>, <a href="https://blog.luevano.xyz/tag/@godot.html">godot</a> </p>
+</div>
+
+ </div>
+ </main>
+
+ <footer>
+<span>
+ <i class="fas fa-address-card" alt="Contact"></i>
+ <a href="https://blog.luevano.xyz/contact.html">Contact</a>
+</span>
+
+<span>
+ <i class="fas fa-donate" alt="Donate"></i>
+ <a href="https://blog.luevano.xyz/donate.html">Donate</a>
+</span>
+
+<span>
+ <i class="fas fa-rss" alt="RSS"></i>
+ <a target="_blank" href="https://blog.luevano.xyz/rss.xml">RSS</a>
+</span>
+
+<br>
+<span class="created-with">
+ <i class="fas fa-hammer" alt="Hammer"></i>
+ Created with <a href="https://github.com/luevano/pyssg">pyssg</a>
+</span>
+
+<br>
+<span class="copyright">
+ Copyright <i class="far fa-copyright" alt="Copyright"></i> 2023 David Luévano Alvarado
+</span>
+
+ </footer>
+ </body>
+</html> \ No newline at end of file
diff --git a/live/blog/g/godot_layers_and_masks_notes.html b/live/blog/g/godot_layers_and_masks_notes.html
index f13805e..1d55d97 100644
--- a/live/blog/g/godot_layers_and_masks_notes.html
+++ b/live/blog/g/godot_layers_and_masks_notes.html
@@ -103,6 +103,12 @@
<p>While the complete answer is the first, as that is how layers work, the second can be used like a rule: 1) the <code>layer</code> is where the object lives, while 2) the <code>mask</code> is what the object will detect.</p>
<div class="page-nav">
+ <span class="next">
+ <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html" alt="Next">
+ <i class="fas fa-arrow-left" alt="Arrow left"></i>
+ <span>Next</span>
+ </a>
+ </span>
<span class="index">
<a href="https://blog.luevano.xyz" alt="Index">
diff --git a/live/blog/index.html b/live/blog/index.html
index 6b32ad9..217ca53 100644
--- a/live/blog/index.html
+++ b/live/blog/index.html
@@ -87,7 +87,9 @@
<h2>Articles</h2>
<ul class="page-list">
- <h3>2023</h3>
+ <h3>2024</h3>
+ <li><span class="page-list-item">Mar 01</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html">Final improvements to the FlappyBird clone and Android support devlog 3</a></li>
+ <h3>2023</h3>
<li><span class="page-list-item">Aug 29</span> - <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">Godot layers and masks notes</a></li>
<li><span class="page-list-item">Aug 27</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">Porting the FlappyBird clone to Godot 4.1 devlog 2</a></li>
<li><span class="page-list-item">Aug 20</span> - <a href="https://blog.luevano.xyz/a/pastebin_alt_with_privatebin.html">Set up a pastebin alternative with PrivateBin and YOURLS</a></li>
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
@@ -23,6 +23,157 @@
<link>https://blog.luevano.xyz</link>
</image>
<item>
+ <title>Final improvements to the FlappyBird clone and Android support devlog 3</title>
+ <link>https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html</link>
+ <guid isPermaLink="true">https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html</guid>
+ <pubDate>Fri, 01 Mar 2024 10:00:57 GMT</pubDate>
+ <category>English</category>
+ <category>Gamedev</category>
+ <category>Gdscript</category>
+ <category>Godot</category>
+ <description>Notes on the final improvements to my FlappyBird clone made in Godot 4.x. Also details on the support for Android.</description>
+ <content:encoded><![CDATA[<p>Decided to conclude my FlappyBird journey with one last set of improvements, following up on devlogs <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html">1</a> and <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">2</a>. Focusing on <strong>refactoring</strong>, <strong>better UI</strong>, <strong>sprite selection</strong> and Android support.</p>
+<p>I missed some features that I really wanted to get in but I&rsquo;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 <em>QoL</em> UI enhancements and extra buttons basically.</p>
+<p>The source code can be found at <a href="https://github.com/luevano/flappybirdgodot">luevano/flappybirdgodot</a>. Playable at <a href="https://lorentzeus.itch.io/flappybirdgodot">itch.io</a>:</p>
+<p style="text-align:center"><iframe src="https://itch.io/embed/1551015?dark=true" width="208" height="167" frameborder="0"><a href="https://lorentzeus.itch.io/flappybirdgodot">FlappyBirdGodot by Lorentzeus</a></iframe></p>
+
+<h2 id="table-of-contents">Table of contents<a class="headerlink" href="#table-of-contents" title="Permanent link">&para;</a></h2>
+<div class="toc">
+<ul>
+<li><a href="#table-of-contents">Table of contents</a></li>
+<li><a href="#refactoring">Refactoring</a></li>
+<li><a href="#improvements">Improvements</a><ul>
+<li><a href="#background-parallax">Background parallax</a></li>
+<li><a href="#sprite-switcher">Sprite switcher</a></li>
+<li><a href="#save-data">Save data</a></li>
+</ul>
+</li>
+<li><a href="#android">Android</a></li>
+<li><a href="#misc">Misc</a></li>
+</ul>
+</div>
+<h2 id="refactoring">Refactoring<a class="headerlink" href="#refactoring" title="Permanent link">&para;</a></h2>
+<p>The first part for my refactor was to move everything out of the <code>src/</code> directory into the root directory of the git repository, organizing it a tiny bit better, personal preference from what I&rsquo;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:</p>
+<ul>
+<li>The way I handled the gameplay means that I needed to make the camera, background and the (ceiling and tiles) &ldquo;detectors&rdquo; move along with the player, while restricting their movement in the <code>x</code> axis, really hacky. Instead, I did what I should&rsquo;ve done from the beginning&hellip; 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 <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html">FlappyBirdgodot devlog 1</a> but didn&rsquo;t actually follow).</li>
+<li>Moved the <code>set_process</code> methodology to their own scripts, instead of handling everything in <code>main.gd</code> while also taking advantage of how signals work now. Instead of doing:</li>
+</ul>
+<pre><code class="language-gdscript">func _ready():
+ Event.game_pause.connect(_on_game_pause)
+
+func _on_game_pause(pause: bool):
+ set_process(pause)
+</code></pre>
+<p>Just connecting to <code>set_process</code> is enough:</p>
+<pre><code class="language-gdscript">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))
+</code></pre>
+<h2 id="improvements">Improvements<a class="headerlink" href="#improvements" title="Permanent link">&para;</a></h2>
+<h3 id="background-parallax">Background parallax<a class="headerlink" href="#background-parallax" title="Permanent link">&para;</a></h3>
+<p>First thing was to add a moving background functionality, by adding 2 of the same <code>Sprite2D</code>&lsquo;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:</p>
+<pre><code class="language-gdscript">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 &lt;= - background_orig.position.x:
+ backgrounds[int(ifirst)].position.x = backgrounds[int(!ifirst)].position.x + texture_size.x
+ ifirst = !ifirst
+</code></pre>
+<p>Then I added background parallax by separating the background sprites in two: background and &ldquo;foreground&rdquo; (the buildings in the original sprites). And to move them separately just applied the same logic described above with 2 different speeds.</p>
+<h3 id="sprite-switcher">Sprite switcher<a class="headerlink" href="#sprite-switcher" title="Permanent link">&para;</a></h3>
+<p>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):</p>
+<pre><code class="language-gdscript">func _get_new_sprite_index(index: int) -&gt; int:
+ return clampi(index, 0, background_textures.size() - 1)
+
+
+func _set_sprites_index(index: int) -&gt; 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
+</code></pre>
+<p>Then, in custom signals I just call <code>_set_sprites_index</code> with a <code>texture_index +/- 1</code>.</p>
+<h3 id="save-data">Save data<a class="headerlink" href="#save-data" title="Permanent link">&para;</a></h3>
+<p>Moved from manual <code>ConfigFile</code> (which is an <code>.ini</code> file basically) to <code>Resource</code> which is easier to work with and faster to implement.</p>
+<p>Accomplished by defining a new <code>data_resource.gd</code>:</p>
+<pre><code class="language-gdscript">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
+</code></pre>
+<p>Where the <code>@export</code>s are not needed unless you need to manage the <code>.tres</code> resource files for testing in-editor.</p>
+<p>Then, the <code>data.gd</code> script needs to be changed accordingly, most notably:</p>
+<ul>
+<li>The file extension is <code>.tres</code> instead of <code>.cfg</code>.</li>
+<li>No need for &ldquo;config sections&rdquo;.</li>
+<li>Saving changes to:</li>
+</ul>
+<pre><code class="language-gdscript">func save():
+ var err: int = ResourceSaver.save(_data, DATA_PATH)
+ if err != OK:
+ print(&quot;[ERROR] Couldn't save data.&quot;)
+</code></pre>
+<ul>
+<li>The loader gets simplified (mostly because the default values are set in the <code>_init()</code> function of the <code>data_resource.gd</code>) to:</li>
+</ul>
+<pre><code class="language-gdscript">func _load_data():
+ if ResourceLoader.exists(DATA_PATH):
+ _data = load(DATA_PATH)
+ else:
+ _data = DataResource.new()
+ save()
+</code></pre>
+<ul>
+<li>The attributes/config/saved data can be retrieved directly by the <code>data_resource.gd</code> variable name, for example: instead of <code>_data.get_value(SCORE_SECTION, "high_score")</code> it&rsquo;s now simply <code>_data.high_score</code>. And similar for setting the values.</li>
+</ul>
+<p>Compared to the <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html#saved-data">3.x version</a> it is a lot more simple. Though I still have setters and getters for each attribute/config (I&rsquo;ll se how to change this in the future).</p>
+<h2 id="android">Android<a class="headerlink" href="#android" title="Permanent link">&para;</a></h2>
+<p>I did add android support but it&rsquo;s been so long since I did it that I actually don&rsquo;t remember (this entry has been sitting in a draft for months). In general I followed the official guide for <a href="https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_android.html">Exporting for Android</a>, setting up Android studio and remotely debugging with my personal phone; it does take a while to setup but after that it&rsquo;s as simple as doing <em>&ldquo;one click deploys&rdquo;</em>.</p>
+<p>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 <em>Project Settings</em> that I remember that needs changes are:</p>
+<ul>
+<li><em>display/window/handheld/orientation</em> set to <code>Portrait</code>.</li>
+<li><em>input_devices/pointing/emulate_touch_from_mouse</em> and <em>input_devices/pointing/emulate_mouse_from_touch</em> both set to <code>on</code>.</li>
+</ul>
+<h2 id="misc">Misc<a class="headerlink" href="#misc" title="Permanent link">&para;</a></h2>
+<p>Found a bug on the <code>ScoreDetector</code> where it would collide with the <code>Ceiling</code>. While this is really not a problem outside of me doing tests I fixed it by <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">applying the correct layer/mask</a>.</p>]]></content:encoded>
+ </item>
+ <item>
<title>Godot layers and masks notes</title>
<link>https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html</link>
<guid isPermaLink="true">https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html</guid>
diff --git a/live/blog/sitemap.xml b/live/blog/sitemap.xml
index 7084a19..cbee393 100644
--- a/live/blog/sitemap.xml
+++ b/live/blog/sitemap.xml
@@ -46,6 +46,12 @@
</url>
<url>
+ <loc>https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html</loc>
+ <lastmod>2024-03-01</lastmod>
+ <changefreq>weekly</changefreq>
+ <priority>1.0</priority>
+ </url>
+ <url>
<loc>https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html</loc>
<lastmod>2023-08-29</lastmod>
<changefreq>weekly</changefreq>
diff --git a/live/blog/tag/@english.html b/live/blog/tag/@english.html
index 10f4aef..88ad98e 100644
--- a/live/blog/tag/@english.html
+++ b/live/blog/tag/@english.html
@@ -77,7 +77,9 @@
<h2>Articles</h2>
<ul class="page-list">
- <h3>2023</h3>
+ <h3>2024</h3>
+ <li><span class="page-list-item">Mar 01</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html">Final improvements to the FlappyBird clone and Android support devlog 3</a></li>
+ <h3>2023</h3>
<li><span class="page-list-item">Aug 29</span> - <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">Godot layers and masks notes</a></li>
<li><span class="page-list-item">Aug 27</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">Porting the FlappyBird clone to Godot 4.1 devlog 2</a></li>
<li><span class="page-list-item">Aug 20</span> - <a href="https://blog.luevano.xyz/a/pastebin_alt_with_privatebin.html">Set up a pastebin alternative with PrivateBin and YOURLS</a></li>
diff --git a/live/blog/tag/@gamedev.html b/live/blog/tag/@gamedev.html
index b4141f6..a4c546d 100644
--- a/live/blog/tag/@gamedev.html
+++ b/live/blog/tag/@gamedev.html
@@ -77,7 +77,9 @@
<h2>Articles</h2>
<ul class="page-list">
- <h3>2023</h3>
+ <h3>2024</h3>
+ <li><span class="page-list-item">Mar 01</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html">Final improvements to the FlappyBird clone and Android support devlog 3</a></li>
+ <h3>2023</h3>
<li><span class="page-list-item">Aug 29</span> - <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">Godot layers and masks notes</a></li>
<li><span class="page-list-item">Aug 27</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">Porting the FlappyBird clone to Godot 4.1 devlog 2</a></li>
<h3>2022</h3>
diff --git a/live/blog/tag/@gdscript.html b/live/blog/tag/@gdscript.html
index 3866b7c..f6540c2 100644
--- a/live/blog/tag/@gdscript.html
+++ b/live/blog/tag/@gdscript.html
@@ -77,7 +77,9 @@
<h2>Articles</h2>
<ul class="page-list">
- <h3>2023</h3>
+ <h3>2024</h3>
+ <li><span class="page-list-item">Mar 01</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html">Final improvements to the FlappyBird clone and Android support devlog 3</a></li>
+ <h3>2023</h3>
<li><span class="page-list-item">Aug 29</span> - <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">Godot layers and masks notes</a></li>
<li><span class="page-list-item">Aug 27</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">Porting the FlappyBird clone to Godot 4.1 devlog 2</a></li>
<h3>2022</h3>
diff --git a/live/blog/tag/@godot.html b/live/blog/tag/@godot.html
index a5f2d67..203ea23 100644
--- a/live/blog/tag/@godot.html
+++ b/live/blog/tag/@godot.html
@@ -77,7 +77,9 @@
<h2>Articles</h2>
<ul class="page-list">
- <h3>2023</h3>
+ <h3>2024</h3>
+ <li><span class="page-list-item">Mar 01</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_3.html">Final improvements to the FlappyBird clone and Android support devlog 3</a></li>
+ <h3>2023</h3>
<li><span class="page-list-item">Aug 29</span> - <a href="https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html">Godot layers and masks notes</a></li>
<li><span class="page-list-item">Aug 27</span> - <a href="https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html">Porting the FlappyBird clone to Godot 4.1 devlog 2</a></li>
<h3>2022</h3>
diff --git a/src/blog/g/flappybird_godot_devlog_3.md b/src/blog/g/flappybird_godot_devlog_3.md
new file mode 100644
index 0000000..e9bca3e
--- /dev/null
+++ b/src/blog/g/flappybird_godot_devlog_3.md
@@ -0,0 +1,170 @@
+title: Final improvements to the FlappyBird clone and Android support devlog 3
+author: David Luévano
+lang: en
+summary: Notes on the final improvements to my FlappyBird clone made in Godot 4.x. Also details on the support for Android.
+tags: gamedev
+ godot
+ gdscript
+ english
+
+Decided to conclude my FlappyBird journey with one last set of improvements, following up on devlogs [1](https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html) and [2](https://blog.luevano.xyz/g/flappybird_godot_devlog_2.html). 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](https://github.com/luevano/flappybirdgodot). Playable at [itch.io](https://lorentzeus.itch.io/flappybirdgodot):
+
+<p style="text-align:center"><iframe src="https://itch.io/embed/1551015?dark=true" width="208" height="167" frameborder="0"><a href="https://lorentzeus.itch.io/flappybirdgodot">FlappyBirdGodot by Lorentzeus</a></iframe></p>
+
+# Table of contents
+
+[TOC]
+
+# 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](https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html) 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:
+
+```gdscript
+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:
+
+```gdscript
+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:
+
+```gdscript
+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):
+
+```gdscript
+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`:
+
+```gdscript
+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 `@export`s 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:
+
+```gdscript
+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:
+
+```gdscript
+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](https://blog.luevano.xyz/g/flappybird_godot_devlog_1.html#saved-data) 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](https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_android.html), 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](https://blog.luevano.xyz/g/godot_layers_and_masks_notes.html).