<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://chsxf.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://chsxf.dev/" rel="alternate" type="text/html" /><updated>2026-04-04T09:29:31+00:00</updated><id>https://chsxf.dev/feed.xml</id><title type="html">chsxf.dev</title><author><name>Christophe SAUVEUR</name></author><entry><title type="html">Building VGTunes</title><link href="https://chsxf.dev/2026/04/04/17-building-vgtunes.html" rel="alternate" type="text/html" title="Building VGTunes" /><published>2026-04-04T00:00:00+00:00</published><updated>2026-04-04T00:00:00+00:00</updated><id>https://chsxf.dev/2026/04/04/17-building-vgtunes</id><content type="html" xml:base="https://chsxf.dev/2026/04/04/17-building-vgtunes.html"><![CDATA[<p>It’s been a long time since I posted on this devlog. It seems that’s happening when you are fully immersed in the last stages of production of a video game.</p>

<p>At the end of 2024, I was stuck in the mud with <a href="https://github.com/chsxf/CiderKit">CiderKit</a>. I wasn’t able to wrap my head around <a href="https://developer.apple.com/documentation/Swift/AdoptingSwift6">Strict Concurrency</a> becoming mandatory with Swift 6, and I wanted to make my project compliant with that. I ended up making some nice refactoring within the map management classes, but forcing myself into it was a mistake. I finally realized I needed a break from all that, and decided to stick with Swift 5 for the time being. And to work on something else for a while. Though, that moment lasted almost 6 months and gave me <a href="https://vgtunes.chsxf.dev">VGTunes</a>.</p>

<p>The first version was released more than a year ago, on January 15th, 2025.</p>

<p><img src="/assets/posts/17/make-it-good-later.jpg" alt="Make it exist first, Make it good later" /></p>

<h1 id="vgwhat">VGWhat?</h1>

<p>I am passionate about pretty much everything in video games. From tech to art to design, I love it all. But, besides the actual games themselves, soundtracks are what I consume the most. And, unfortunately, video game soundtracks, however famous they may be today, did not always receive the recognition they deserved. Case in point, most streaming platforms lack a dedicated “Video Games Music” category, or at best have a curated subsection that contains only a few entries. I always said to myself that, given the opportunity, I would work towards a better platform for video game music.</p>

<p>Another aspect of today’s digital music landscape, one that is not tied to video game music specifically, is that many people are using either <a href="https://open.spotify.com">Spotify</a> or <a href="https://music.apple.com">Apple Music</a>. That’s not my case, and I became somewhat tired of being a <a href="https://deezer.com">Deezer</a><sup id="fnref:deezer" role="doc-noteref"><a href="#fn:deezer" class="footnote" rel="footnote">1</a></sup> user confronted with another significant part of the world sharing links from other services, making them essentially useless for me. What if there were a website listing video game soundtracks and their location on the various streaming platforms? A database of streamable video game soundtracks if you will.</p>

<p>That became the starting point for <a href="https://vgtunes.chsxf.dev">VGTunes</a>, a website where you can find video game <em>(hence the VG)</em> music <em>(Tunes)</em>, and where it is streamable from. Plus other infos, like the Steam page for the game.</p>

<h1 id="finding-the-intersection">Finding the Intersection</h1>

<p>Other people are already building similar things.</p>

<p>First, you have individuals building more editorial oriented websites like <a href="https://nowplaying.cool/">NOWPLAYING</a>. But that’s not my goal. I’m lacking the time for such authored content.</p>

<p>On one hand, you have <a href="https://vgmdb.net">VGMdb</a>: a proper database of everything video game music, including physical editions. The website is fairly complete, with detailed information on every entry. Though in many cases, links to streaming services are missing. This is the reference website and my main target. I hope at some point in the future <a href="https://vgtunes.chsxf.dev">VGTunes</a> will be as detailed and complete.</p>

<p>On the other hand, you have social websites like <a href="https://odesli.co/">Songlink/Odesli</a> and <a href="https://li.sten.to/">Listen.to</a>. However, these websites are not databases. They only allow people and artists to create custom webpages with links to the various avenues where a specific album or song is available, acting as a single URL to use on their website.</p>

<p><a href="https://vgtunes.chsxf.dev">VGTunes</a> is basically a mix of these. It’s a database, expected to be as complete as possible, but with the design philosophy of the social websites.</p>

<p><img src="/assets//posts/17/link-example.jpg" alt="Moss II VGTunes Link Example" /></p>

<h1 id="starting-small">Starting Small</h1>

<p>As I write this post, <a href="https://vgtunes.chsxf.dev">VGTunes</a> references more than 2,300 albums and 1,200 artists. More than a year after the start of the project, it’s already a substantial database. Of course, it is nowhere near completion as I had to start from nothing.</p>

<p>How do you fill up a database of streamable video game soundtracks? For some reason, streaming platforms have no comprehensive way to search by genre, or even a dedicated genre for video game soundtracks. Even worse, some albums have no genre attached. That couldn’t be the starting point, nor a valid source for automation.</p>

<p>I turned to VGMdb and search for an API. In late 2024, nothing concrete was available. Right now, in April 2026, an API seems to be on its way, but is still in private beta and unvailable to me at the moment.</p>

<p>So, I had to build my own tools to populate the database.</p>

<h1 id="tooling-up">Tooling Up</h1>

<p>I took a very naive approach: search the streaming platforms for generic keywords (“original game soundtrack” for example) and add each result manually. The very first version of this search/add tool was very rudimentary. The tool was searching on Deezer and I had to manually get the IDs for other platforms by hand (Apple Music and Spotify only at the time).</p>

<p>At the time of writing, the website supports Apple Music, <a href="https://bandcamp.com/">Bandcamp</a>, Deezer, Spotify, <a href="https://tidal.com/">Tidal</a>, as well as <a href="https://store.steampowered.com/">Steam</a> and links to Steam soundtracks when available. It was unimaginable to be able to scale with such limited helpers.</p>

<p>So, I refined the process over time to automate as many steps as possible. The starting point is still pretty much the same: search for an album on one of the platforms and select it for inclusion in the database. But after that, the current backend of <a href="https://vgtunes.chsxf.dev">VGTunes</a> actually scans other platforms for similar results and automatically link albums if an exact match is found. It saves me a lot of time and helped grow the database much faster. It is still far from perfect, but it works.</p>

<p><img src="/assets/posts/17/vgtunes-dashboard.jpg" alt="VGTunes Dashboard" />
<em>VGTunes Backend Dashboard</em></p>

<p>I also created a website generator (as the front-end of <a href="https://vgtunes.chsxf.dev">VGTunes</a> is basically a static website) that takes information from the database and creates the various web pages for every referenced album and artist, including alphabetical catalog pages.</p>

<h1 id="whats-next">What’s Next?</h1>

<p>There is probably a visual redesign down the line for <a href="https://vgtunes.chsxf.dev">VGTunes</a>. And more platforms added to it, like <a href="https://www.qobuz.com">Qobuz</a>, <a href="https://music.youtube.com/">YouTube Music</a>, and others.</p>

<p>On the feature side, I would like to create browser extensions that would allow sharing <a href="https://vgtunes.chsxf.dev">VGTunes</a> links when listening to a referenced album on the supported streaming platforms.</p>

<p>Only time will tell what comes first.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:deezer" role="doc-endnote">
      <p>About a year after Spotify was founded in 2006, a company called Deezer was born in France. And it became progressively very popular in the country, in part due to the fact that the first french internet service provider at the time (Orange) got invested in it. Being french, I became a subscriber a long time ago (at the same time as my internet service) and, after all these years, this is still the streaming platform from which I get my music. <em>And, of course, there are also so <a href="https://time.com/6144634/artists-boycott-spotify-joe-rogan/">many</a> <a href="https://www.latimes.com/entertainment-arts/music/story/2025-07-31/spotifys-ceo-owns-an-ai-weapons-company-some-musicians-say-its-time-to-leave">legitimate</a> <a href="https://www.forbes.com/sites/bernardmarr/2025/03/04/spotifys-bold-ai-gamble-could-disrupt-the-entire-music-industry/">reasons</a> not to subscribe to Spotify.</em> <a href="#fnref:deezer" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Christophe SAUVEUR</name></author><category term="VGTunes" /><summary type="html"><![CDATA[At the end of 2024, I was stuck in the mud with Swift's String Concurrency. After some time, I finally realized I needed a break from all that for a while. That "while" lasted almost 6 months and resulted in VGTunes. In this post, I will talk about the creation of my own video game music database website.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/17/make-it-good-later.jpg" /><media:content medium="image" url="https://chsxf.dev/assets/posts/17/make-it-good-later.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">My First Experience With Godot</title><link href="https://chsxf.dev/2024/09/13/16-first-experience-with-godot.html" rel="alternate" type="text/html" title="My First Experience With Godot" /><published>2024-09-13T00:00:00+00:00</published><updated>2024-09-13T00:00:00+00:00</updated><id>https://chsxf.dev/2024/09/13/16-first-experience-with-godot</id><content type="html" xml:base="https://chsxf.dev/2024/09/13/16-first-experience-with-godot.html"><![CDATA[<p>A few weeks ago was held the <a href="https://itch.io/jam/gmtk-2024">2024 edition of the GMTK Game Jam</a>. And, like <a href="https://chsxf.itch.io/top-and-tom">previous</a> <a href="https://chsxf.itch.io/top-and-tom">years</a>, I participated and released a game: <a href="https://chsxf.itch.io/web-cycles">Web Cycles</a>. However, this year, I put up a complementary challenge: using <a href="https://godotengine.org/">Godot</a>. Here’s a small post-mortem of my first eperience with the engine.</p>

<p><img src="/assets/posts/16/godot.jpg" alt="" /></p>

<h1 id="my-motivations">My Motivations</h1>

<p>I’ve been using Unity since 2013 almost daily, at work and during game jams. Even though I’ve moved to SpriteKit for my personal projects, it cannot export games to Windows or for the web. WebGL exports are especially important for game jams as it allows people to safely play games with even installing them. So far, Unity was the perfect candidate.</p>

<p>However, in september last year, management at Unity decided to introduce a new way of monetizing the engine: the <a href="https://unity.com/fr/products/pricing-updates">Unity Runtime Fee</a>. It would be an understatement to say that developers around the world were disappointed. The first proposed solution was an insult to every game developer that was using the engine. Luckily, the final draft was much more sensible, but a lot of trust has been lost along the way. And Unity has a long road ahead to regain that, even if the Runtime Fee has finally <a href="https://unity.com/fr/blog/unity-is-canceling-the-runtime-fee">been canceled</a>.</p>

<p>Enters Godot. The same day Unity announced their new fee, Godot introduced its <a href="https://godotengine.org/article/godot-developer-fund/">development fund</a>. Unity’s bad move gave it a lot of attention as many indie devs and small to medium studios looked for an alternative to Unity. And so did I!</p>

<p>But it can be pretty hard while working to learn new technologies that are not directly applicable to the production at hand. That’s why I decided to give Godot a try during my favorite game jam of the year, to see for myself if there’s any potential for me or for the studio in the future.</p>

<p><em>(Alt Shift, the studio I’m part of, still uses Unity and will continue in the foreseable future. This post, like all the content of this website, exposes only my personal views)</em></p>

<h1 id="a-bit-of-disclaimer">A Bit of Disclaimer</h1>

<p>As I said earlier, I’ve used Unity for more than ten years now and, during this time, exclusively with C#. The fact that Godot embraced C# officially a few years ago makes it a very good alternative choice over Unreal that uses C++. In order to avoid starting completely fresh during the game jam, I decided to follow a few tutorials (notably <a href="https://www.youtube.com/watch?v=LOhfqjmasi0">from Brackeys</a>) to get familiar with the editor and the components. The tutorial uses GDScript, Godot’s first-party scripting language, but I converted most code pretty easily to C#.</p>

<script>
    if (!document.youtubeScriptAdded) {
        document.youtubeIds = [];
        document.youtubeScriptAdded = true;

        var tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        function onYouTubeIframeAPIReady() {
            for (var videoId of document.youtubeIds) {
                var playerId = `player_${videoId}`;
                var playerElement = document.getElementById(playerId);
                var playerWidth = playerElement.offsetWidth;
                var playerHeight = Math.ceil(playerWidth / 16 * 9);

                var player = new YT.Player(`player_${videoId}`, {
                    height: playerHeight,
                    width: playerWidth,
                    videoId: videoId,
                    playerVars: {
                        autoplay: 0,
                        modestbranding: 1,
                        rel: 0
                    }
                });
            }
        }
    }

    document.youtubeIds.push("LOhfqjmasi0");
</script>

<div id="player_LOhfqjmasi0" class="youtube-video-player"></div>

<p>Unfortunately, I discovered a few days before the game jam that the WebGL export template for Godot wasn’t available in the dotnet version of the engine. If I wanted to export for the web, I would be stuck with GDScript. Many people consider GDScript a very good choice for Godot, so I decided to take the bullet and went with it.</p>

<p>So basically, everything that follows is me trying my best to code a game in about 96 hours, while discovering pretty much everything about the engine. I’m certain some of my conclusions are wrong based on that, and that solutions exist that I didn’t find by myself.</p>

<h1 id="the-good-bits">The Good Bits</h1>

<h2 id="light-but-mighty">Light but Mighty</h2>

<p>Fortunately, many things are advocating for Godot from the get go. For starters, the editor / engine is very small. About 250 MB on macOS, as a Universal binary which supports x86_64 as well as Apple Silicon architectures. That’s impressive compared to the 8+ GB of the Unity Editor 6000. Sure, you have to install the dotnet runtime by yourself if you want to use the corresponding version, but that’s a minor issue and does not explain the size difference.</p>

<p>Of course, the editor is in itself way less demanding on the system (putting aside the memory required for your project). This is especially important for me as I sometimes use a Intel MacBook Air 2020 with 8 GB of RAM and it struggles to handle Unity and Rider simultaneously (as Rider is a memory hog). Godot + Visual Studio Code would be a breath of fresh air!</p>

<p>Projects are also way smaller, as there is no Library folder. These special Unity folders, present in every project, can take a lot of space. For example, for my GMTK Jam 2023 project, the overall size of the Unity project folder is about 2 GB, from which the Library folder takes 1.37 GB. And, obviously, the bigger the project, the bigger the Library folder.</p>

<p>The only downsides of the small size of Godot is that you have to download export templates separately (and for all platforms at once). This download weighs about 1 GB.</p>

<h2 id="complete-editor">Complete Editor</h2>

<p>Just like Unity, the Godot editor is mostly fully featured, with views for the scene / world, the hierarchy, an inspector, animation tools, etc. I have not explored the full panel, but expect for coding, I never had to leave the editor while building my game.</p>

<p><img src="/assets/posts/16/godot-editor.png" alt="Godot Engine Editor" /></p>

<p>Godot also includes a debugger, that is perfectible but totally serviceable anyway. However, the main difference with Unity is that the game does not run inside the editor itself. It runs in another instance of the player and the editor / debugger connects to it remotely (hance the Remote view you use to see the current hierarchy, and to inspect objects).</p>

<p>There are also many types of nodes already provided by Godot. Many of my use cases during the game jam have been covered from the get go.</p>

<h2 id="one-node-one-purpose">One Node, One Purpose</h2>

<p>In Unity, the scene tree is composed of Game Objects, that then hold multiple components to give them purpose. That works but this is not ideal. It becomes quickly difficult to identify which object holds what components, and we sometimes have to create one Game Object per component (generally global managers) and name them accordingly. That clearly defeats the purpose.</p>

<p>Godot took a drastically different approach. Every object in the scene tree is a node, and each node has one purpose only. Each built-in node has a specific icon in the scene tree that helps identification even more (it is probably possible for custom nodes, but I didn’t dig into that yet). Working with objects in the hierarchy has never been easier!</p>

<h2 id="call-down-signal-up">Call Down, Signal Up</h2>

<p>This design pattern is not specific to Godot as this is a best practice in many cases.</p>

<p>Basically, within a hierarchy of objects, if you want to communicate things from a parent to a child node, you just have to call a method on one of the child nodes, because parents generally know how their children are organized. In the other direction, as children have no knowledge of their parent’s structure and ancestry, calling methods generally ends up in a beautiful mess.</p>

<p>Enter events. Or signals. Or whatever you call them. Children are supposed to emit a signal or invoke an event to notify one of their parents that something notable has happened. Communication between sibling nodes should also pass through signals or events: one sibling emits a signal, the parent catches the signal, then calls the right method on another child sibling node.</p>

<p>This design can (and should) be implemented in any engine. Unity included. However, it is especially embroiled in Godot’s DNA as, by default, you can’t see the child nodes of a scene nested inside another. In Unity, all children of a prefab are visible by default if you expand the corresponding hierarchy. That often leads to unfortunate connections between GameObjects and components that should never have been linked. By hiding the child nodes, Godot does not train you to connect them in unwanted fashion. If you decide to engage with a worse practice, you have to want it and act accordingly. Therefore, my Godot project instantly felt more organized than any of my previous Unity projects.</p>

<h1 id="not-all-roses">Not All Roses</h1>

<p>Unfortunately, for me at least, everything is not perfect within Godot.</p>

<h2 id="a-node-path-is-not-a-node-reference">A Node Path is Not a Node Reference</h2>

<p>When using GDScript, Godot will encourage you to use <code class="language-plaintext highlighter-rouge">@onready</code> variables. They are basically variables that are initialized when the object is ready to be used. These variables are initialized with a reference to another node thanks to a relative path.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@onready var my_node: Node = $relative/path/to/my/Node
</code></pre></div></div>

<p>However, as the path to the referenced node is hardcoded into your scripts, they won’t be updated if you change the name or position of the referenced node in the hierarchy, or of any of its ancestors. And if you’re not careful, that’s something that will come to bite you at runtime the next time you run your game. If the problem is in a less frequently tested area, it may be missed. Be careful.</p>

<p>There is no such things in Unity. We have serialized fields in the inspector, but that would be <code class="language-plaintext highlighter-rouge">@export</code> variables in Godot, for which the problem doesn’t seem to occur. They are updated by the editor when moved or renamed. I would recommend to avoid <code class="language-plaintext highlighter-rouge">@onready</code> variables when possible, especially on complex trees.</p>

<h2 id="a-resourceful-mess">A Resourceful Mess</h2>

<p>Storing structured data with Unity is simple. Create a class deriving from ScriptableObject, then create an asset based on it, and you’re almost good to go.</p>

<p>Godot has a similar mechanism called Resources. You create a class deriving from Resource and you can then create an asset based on it. However, in my brief experience, this was way more complicated and unstable than with Unity. I had to reboot the editor multiple times to see my changes reflected in the structure of my resources. That may be due to the fact that I was using Rider as an external text editor, but that was very frustrating. In the end, to save some time, I just used JSON files and a simple function to parse them. But I lost the ability to edit them in the inspector.</p>

<h2 id="lost-time">Lost Time</h2>

<p>Speaking of unstability or inconsistencies, I also encountered several issues while trying to setup an external editor, wether it’s Rider or Visual Studio Code. Procedures are well described for the two IDEs, but in both cases, each time I edited a file, I was granted with a box asking me to reload the file or use the one in memory because it had been edited outside of Godot itself. What is the point of defining an external text editor if you can’t reload the files automatically? Turns out there is a totally different setting just for that that is not enabled by default when an external text editor is defined. That should be the case in my opinion.</p>

<p>But, even with that, after having set up Visual Studio Code completely, I was unable to edit my JSON files. Godot would refuse to load them and each time went back to the state it had in memory. I lost half an hour on this strange behaviour, that doesn’t seem to occur with Rider. I haven’t tried to reproduce it since then, so there was maybe a glitch somewhere, but it was definitely not a smooth ride with external editors. Which is a shame because the built-in editor is missing critical tools, notably for refactoring.</p>

<h2 id="where-are-my-sprites">Where Are My Sprites?</h2>

<p>For my project, I used Sprite2D nodes. I was expecting something similar to Unity when you can create sprites within a sprite sheet in advance and use them directly within your Sprite renderer. For the life of me, I wasn’t able to find an equivalent in Godot and had to fall back to use a texture region, which was OK but way less convenient, especially when you want to change the sprite of your node at runtime. I had to manually input the regions for each of my sprites in my JSON configuration file to be able to do that. What a waste of time.</p>

<h2 id="obscure-ui-nodes">Obscure UI Nodes</h2>

<p>The history of UI components within Unity has been a long-running gag among developers. For a long time, we had no built-in UI framework, and then we got Unity UI during the 4.x version cycle. And several years later, Unity UI was deprecated in favor of UI Elements. But, even now, the new framework is not completely ready yet and not backward compatible. So many people remained with the former so far.</p>

<p>Unity UI is a weird framework with its own take on many aspects of the UI. But it’s functional and well known, even if it has its drawbacks, especially with layouts. However, there is something that it does very well, and this is anchoring. Thanks to Canvas and RectTransform components, you can basically place any object anywhere you like on the screen and it will adapt to any resolution and aspect ratio.</p>

<p>Godot seems to have a similar approach with its UI nodes. But, the anchoring system seemed much more rudimentary to me. I had a hard time with the UI and ended up compromising what I had in mind to adapt to what I was able to do. I used many Margin Containers to position my objects, but for some things, that didn’t sound right. I clearly need more practice with it to see if the problems comes from me or from a lack of functionality in the built-in nodes.</p>

<h2 id="gdscript-is-definitely-not-c">GDScript is Definitely Not C#</h2>

<p>I’m pretty sure it will come as a very unpopular opinion, but I am not a fan of GDScript. To be clear, GDScript seems a very capable language and is tightly integrated with the rest of the editor. But it doesn’t align well with my personal preferences.</p>

<p>First, this is a language that uses <a href="https://en.wikipedia.org/wiki/Off-side_rule">significant identation</a>. In other words, it defines blocks using indentation. While it is said to make things more readable, in my experience that’s not the case. I’m probably biased after 30 years using C-based languages, but it is really hard for me to identify where blocks end at a glance, especially if you insert blank lines between blocks (I do that a lot to separate groups of closely related instructions).</p>

<p>Second, this is an interpreted language. The Godot editor or the various static analyzers do their job to identify code that won’t compile, but there were a lot of occurences in my code where it wasn’t possible, mostly when using dictionaries or variants, or when emiting signals. Dictionaries can’t be typed, variants can accept every type, and emiting signals seems a place where analyzers should be able to detect parameters types, but they don’t. In these cases, you will identify issues only when running the game. I would really love to get typed dictionaries and a way to parse JSON to a known class so that values you get in both cases are typed too. It reminded my of the dark old times of early PHP 5 in many regards.</p>

<p>Finally, the Godot editor does not provide any unit test for GDScript. There are <a href="https://github.com/bitwes/Gut">plugins</a> for that obviously, but in my opinion, it should be an integral part of the editor flow, just like we have the Test Runner in Unity.</p>

<h1 id="my-humble-conclusion">My Humble Conclusion</h1>

<p>Godot is better than Unity, on many levels. The introduction of C# as a first-class citizen changed a lot of things for me when considering an engine. It would make the transition from Unity way easier for us as a studio, as we could bring with us most of the engine-agnostic code we created over the years. As for the type of games we do at Alt Shift, or for the scope of games I do during game jams, it seems a very good match.</p>

<p>However, there are some things that need to be addressed first, some things that we take for granted when working with Godot’s competitors.</p>

<p>C# support is still experimental on mobile, even though the actual limitations are not explicitly defined <a href="https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/index.html#c-platform-support">in the documentation</a>. Also, WebGL exports are not compatible with C#. That may seem like a minor things, but playing games in the browser has never been so popular, especially in countries with limited access to internet or where app stores are not available. We can’t code our games twice for that.</p>

<p>The several issues I got with external editors have to be ironed out. Due to the limitations of the internal editor when it comes to C#, this is really important. Anyone willing to do serious work with Godot would want to work in a dedicated code editor, like VSCode or Rider.</p>

<p>Also, Godot has to provide an easier way to manipulate an important quantity of data. Resources don’t work well. Parsing JSON files without validation/mapping is not sustainable.</p>

<p>One thing is now obvious, migrating to Godot from Unity is not easy. Not all your experience will be translated in the process. You’ll have to rebuild a significant part of your knowledge base. But that’s probably true to every transition between engines. At least, the presence of C# means you won’t need to learn a new language, only new APIs.</p>

<p>As for me, I will continue to test Godot during game jams in the years to come. It was fun to use and a breath of fresh air to learn new tools. I recommend everyone to give it a try anyway.</p>]]></content><author><name>Christophe SAUVEUR</name></author><category term="Godot" /><category term="Game Dev" /><category term="GMTK Jam" /><summary type="html"><![CDATA[A few weeks ago was held the 2024 edition of the GMTK Game Jam. I participated and released a game. However, this year, I put up a complementary challenge: using the Godot game engine. In this post, I explain how this first experience went.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/16/godot.jpg" /><media:content medium="image" url="https://chsxf.dev/assets/posts/16/godot.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Releasing MFX</title><link href="https://chsxf.dev/2024/07/12/15-releasing-mfx.html" rel="alternate" type="text/html" title="Releasing MFX" /><published>2024-07-12T00:00:00+00:00</published><updated>2024-07-12T00:00:00+00:00</updated><id>https://chsxf.dev/2024/07/12/15-releasing-mfx</id><content type="html" xml:base="https://chsxf.dev/2024/07/12/15-releasing-mfx.html"><![CDATA[<p><img src="/assets/posts/15/releasing-mfx.png" alt="Example PHP code" /></p>

<p>A few days ago, I released <a href="https://github.com/chsxf/mfx">MFX</a>, a PHP micro-framework I’ve been working on for the past 10 years. Releasing something to the public can be daunting, but in my humble opinion is necessary to bring your creation to its next stage. And, as you’ll see in this post, feedback was very useful.</p>

<h1 id="story-time">Story Time</h1>

<p>I’ve started programming in PHP in 2004, 20+ years ago. Back then, most web hosting providers still used PHP 4, even though PHP 5 was around the corner. PHP 4 had minimalistic object-oriented programming capabilities and no strong typing. PHP 5 was the answer to that.</p>

<p>The new version introduced so many fundational changes that the transition took a long time. However, by the time I changed employer, at the end of 2008, PHP 5 was well established. PHP was then really popular and, moving towards a more strongly typed language, my interest grew as well.</p>

<p>I stayed in this job for 5 years, until my departure in late 2013. The primary focus of the company was browser games. Therefore, I learned a lot of useful lessons about writing high availability websites and APIs with PHP. This was especially true since most of our tools were developed in-house. Errors were made along the way, obviously, but I tried my best, in my position of senior developer, to improve efficiency and reliability along the years. The company ended up with a battle-tested set of tools and a nano-framework as the core of each one of its websites.</p>

<p>After I left, we decided with former colleagues to create our own game studio: Cheese Burgames<sup id="fnref:cbg-closure" role="doc-noteref"><a href="#fn:cbg-closure" class="footnote" rel="footnote">1</a></sup>. We needed some internal back-end tools to support many aspects or our games (localization, game design databases, etc). Of course, I was going to write them in PHP. Nodejs was already a thing, but I could not stand JavaScript (and TypeScript was not mature yet). And so the first stone of MFX was laid.</p>

<h1 id="fast-forward-to-2016">Fast Forward to 2016</h1>

<p>For three years, Cheese Burgames became my sole occupation. During that time, step by step, MFX has seen a lot of evolution. But, as the sole developer in our team of three, I had to handle the development of our games with Unity in C# as well. And MFX remained an internal project. We never actually used it for any of our clients.</p>

<p>But in 2016, after about a year trying to find a publisher for our game <a href="https://chsxf.itch.io/revery-duel-of-dreamers">Revery - Duel of Dreamers</a>, we had to come to the conclusion that the company wouldn’t go for long anymore. And that’s when I got in touch with my current employer, <a href="https://altshift.fr">Alt Shift</a>. I was about to became a senior Unity C# developer only, as PHP was not used in the company. That should have been the end of MFX.</p>

<p>However, a few years later, in 2019, we had to build a back-end and an API for one of our clients and I decided to use MFX. It was the opportunity I had missed so far to validate in the field that my design choices were valid. And MFX delivered! We stopped working with this client a year and a half later due to a change in their priorities, but I had my answers.</p>

<h1 id="the-road-to-release">The Road to Release</h1>

<p>Thanks to that external project, MFX was fine. But it remained an internal tool in essence, even though I started writing some documentation. And after that, we never took a PHP gig with another client. The framework’s code was already available on GitHub in 2018 under a MIT license, but it was pretty much unusable without my support.</p>

<p>So I started to slowly extend the documentation. In 2022, I even worked with a former Alt Shift colleague who wanted to use MFX for the website he was building. He has worked with me on the external project involving MFX and was comfortable enough to consider using it on his own products. I took the opportunity to add support for PHP 8, which became mandatory, and for Composer as well. I also fixed some bugs my friend identified along the way.</p>

<p>Over the past year however, the main road blocks remained the missing parts of the documentation. But, writing page after page, I gradually reached completion. It’s not perfect. It’s probably missing some valuable information. But it’s there. Only remained pressing the button.</p>

<h1 id="mfx-10-is-out">MFX 1.0 is Out!</h1>

<p>On July 9th, 2024, I <a href="https://github.com/chsxf/mfx/discussions/25">released MFX on GitHub</a>, and published some messages on my socials and on Reddit in the <a href="https://www.reddit.com/r/PHP/comments/1dywwl8/mfx_10_is_out/">PHP subreddit</a>. And boy, I got a lot of feedback. Some <a href="https://www.reddit.com/r/PHP/comments/1dywwl8/comment/lcbm29o/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">good</a>, some even <a href="https://www.reddit.com/r/PHP/comments/1dywwl8/comment/lcc6ixt/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">very good</a>. And some <a href="https://www.reddit.com/r/PHP/comments/1dywwl8/comment/lcdba7v/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">not so much</a>.</p>

<p>But that’s the risk I was willing to take. So far, I’ve almost been the sole developer of MFX. I needed a fresh perspective on it. And I was glad to be criticized this way. In the days that followed, I managed to improve syntax consistency, ease of use, security, and even add Docker support.</p>

<h1 id="whats-next">What’s Next?</h1>

<p>Releasing a first version is one thing, but you need to keep it alive. Fortunately, I am at a stage in CiderKit’s development where I will soon need tools to support some aspects of the games. My plan is to recover the source code of the tools I made for Cheese Burgames and update them for the new version of MFX.</p>

<p>Some new features and improvements are also planned for MFX on its own, but the complete list will probably evolve. And of course, I hope some people will try the framework and make some contributions.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:cbg-closure" role="doc-endnote">
      <p>mostly dormant after 2017 and closed since 2020. <a href="#fnref:cbg-closure" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Christophe SAUVEUR</name></author><category term="PHP" /><category term="MFX" /><summary type="html"><![CDATA[A few days ago, I released MFX, a PHP micro-framework I've been working on for the past 10 years. In this post, I will tell you the story of this project and how its 1.0 release has been received.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/15/releasing-mfx.png" /><media:content medium="image" url="https://chsxf.dev/assets/posts/15/releasing-mfx.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">PlayDate with Visual Studio Code</title><link href="https://chsxf.dev/2024/03/11/14-playdate-with-vscode.html" rel="alternate" type="text/html" title="PlayDate with Visual Studio Code" /><published>2024-03-11T00:00:00+00:00</published><updated>2024-07-12T00:00:00+00:00</updated><id>https://chsxf.dev/2024/03/11/14-playdate-with-vscode</id><content type="html" xml:base="https://chsxf.dev/2024/03/11/14-playdate-with-vscode.html"><![CDATA[<p><img src="/assets/posts/14/playdate-w-vscode.png" alt="PlayData console with Visual Studio Code logo" /></p>

<p><strong>July 2024 Update: Since the redaction of this post, Apple has released a <a href="https://github.com/apple/swift-playdate-examples">sample project</a> that allows building games for the PlayDate with Swift. After a bit of exploration, it turned out it was working very well. Going forward, I will use Swift as my language of choice for the PlayDate with <a href="https://github.com/finnvoor/PlaydateKit">PlayDateKit</a>. Therefore, you should not expect any further progress with this tutorial.</strong></p>

<p>For once, I decided to explore a different platform. SpriteKit isn’t going away, but for some things in my regular job, I wanted to see how difficult it would be to develop games for the <a href="https://play.date/">PlayDate</a> in C. In case you’re wondering now, no, Alt Shift has no plan to release a PlayDate game any time soon, but we like to explore different things from time to time.</p>

<h1 id="the-setup">The Setup</h1>

<p>During my initial setup of the PlayDate SDK, I ended up with step-by-step guides that are not cross-platform for the most part. Through the <a href="https://sdk.play.date/2.4.1/Inside%20Playdate%20with%20C.html">official documentation</a>, you can either use <a href="https://developer.apple.com/xcode/">Xcode</a> on macOS, <a href="https://visualstudio.microsoft.com/">Visual Studio</a> on Windows, or <a href="https://www.jetbrains.com/clion">CLion</a> on both platforms. At Alt Shift we use both Windows and macOS, so the only viable option there would be CLion, which is paid (and not cheap).</p>

<p><a href="https://www.jetbrains.com/">Jetbrains</a>, the developers of CLion also make Rider that we use daily, so I have no problem considering CLion as a very capable IDE. But the cost remains a problem if the tool becomes an integral part of your development workflow on a platform as niche as the PlayDate. You may not recover your investment.</p>

<p>In the past, we used <a href="https://code.visualstudio.com/">Visual Studio Code</a> on both Windows and macOS to develop our Unity games. Visual Studio Code being as versatile as it is, I was pretty sure it would be great for PlayDate. But I had a hard time finding any tutorial on how to set it up. There are templates but most do not come with explainers. So I had to build one myself.</p>

<p>Here it is!</p>

<h2 id="disclaimer">Disclaimer</h2>

<p>This tutorial has not been tested outside of macOS (for now). Furthermore, I only used the PlayDate Simulator so far. Adjustements are probably needed to build and debug on actual devices. I will update this post with additional instructions for Windows when time comes if needed.</p>

<h1 id="sample-project">Sample Project</h1>

<p>I found that the “Hello World” sample project from the PlayDate SDK was a good starting project to ensure that your Visual Studio Code is fine. So I’ll use it for the rest of this post.</p>

<p>You can find it under <code class="language-plaintext highlighter-rouge">C_API/Examples/Hello World</code> in the PlayDate SDK folder. Copy it anywhere you want to get started.</p>

<h1 id="dependencies">Dependencies</h1>

<p>Setting up Visual Studio Code for the PlayDate requires a few things only. If you’ve installed all dependencies needed by the PlayDate SDK for the C language, you’ll only need the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools">C/C++ extension</a> from Microsoft (but you can opt for the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack">C/C++ Extension Pack</a> that comes with an additional theme).</p>

<p>To keep things simple, I chose to use plain <a href="https://en.wikipedia.org/wiki/Make_(software)">Makefiles</a> to handle compilation. But your mileage may vary here and you may want to install specific extensions for <a href="https://en.wikipedia.org/wiki/CMake">CMake</a> or any other fancier build tool.</p>

<p>Don’t forget to set the <code class="language-plaintext highlighter-rouge">PLAYDATE_SDK_PATH</code> environment variable. You will need it later.</p>

<h1 id="project-settings">Project Settings</h1>

<p>First, we have to add a few options to <code class="language-plaintext highlighter-rouge">.vscode/settings.json</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"C_Cpp.default.includePath"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"${env:PLAYDATE_SDK_PATH}/C_API"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"C_Cpp.default.defines"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"TARGET_EXTENSION"</span><span class="p">,</span><span class="w"> </span><span class="s2">"PLAYDATE_SIMULATOR"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"[c]"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"editor.defaultFormatter"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ms-vscode.cpptools"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"C_Cpp.formatting"</span><span class="p">:</span><span class="w"> </span><span class="s2">"vcFormat"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"files.associations"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"*.h"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<ul>
  <li>With <code class="language-plaintext highlighter-rouge">C_Cpp.default.includePath</code>, we add the PlayDate C API folder to the include path to be able to use the PlayDate SDK headers without Visual Studio Code complaining.</li>
  <li>With <code class="language-plaintext highlighter-rouge">C_Cpp.default.defines</code>, we set the defines required to compile and debug for the simulator.</li>
  <li>Other settings are here to help formatting <code class="language-plaintext highlighter-rouge">.c</code> and <code class="language-plaintext highlighter-rouge">.h</code> files.</li>
</ul>

<p>With the project settings in place, you should be able to get auto-completion running and be able to navigate your code. Though you won’t be able to build. Yet.</p>

<h1 id="build-and-launch-tasks">Build and Launch Tasks</h1>

<p>In order to actually compile the project and run it on the simulator, we need to update two other files.</p>

<p>First, in <code class="language-plaintext highlighter-rouge">.vscode/tasks.json</code>, we will add a “make” task. As its name suggests, the task will build the project.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"tasks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"make"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"shell"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"make"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"problemMatcher"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
      </span><span class="nl">"group"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"build"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"isDefault"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Then, in <code class="language-plaintext highlighter-rouge">.vscode/launch.json</code>, we will add a launch configuration that will run the build on the simulator, executing the “make” task beforehand to make sure the build is up to date.</p>

<p><strong>Important Note:</strong> Be sure to change <code class="language-plaintext highlighter-rouge">HelloWorld.pdx</code> to the actual name of your build when needed.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.2.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"configurations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Debug"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cppdbg"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"request"</span><span class="p">:</span><span class="w"> </span><span class="s2">"launch"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"program"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${env:PLAYDATE_SDK_PATH}/bin/Playdate Simulator.app"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"${workspaceFolder}/HelloWorld.pdx"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"stopAtEntry"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${fileDirname}"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"environment"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
      </span><span class="nl">"externalConsole"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
      </span><span class="nl">"MIMode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"lldb"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"preLaunchTask"</span><span class="p">:</span><span class="w"> </span><span class="s2">"make"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h1 id="first-run">First Run</h1>

<p>Now that everything is in place, press F5 to build your project and run it. You should see the console display some messages and after a few seconds an “Hello World” message should appear on the simulator’s screen. If not, then, start again, from the top.</p>

<p><img src="/assets/posts/14/playdate-simulator.png" alt="" /></p>

<p>Otherwise, you’re almost good to go. There are only a few more things to set up and call it a day.</p>

<h1 id="additional-settings">Additional Settings</h1>

<h2 id="formatting">Formatting</h2>

<p>While working with Visual Studio Code, you may want to auto-format your files when you’re saving.</p>

<p>For that, you have to add a <code class="language-plaintext highlighter-rouge">.editorconfig</code> file at the root of your project. You can either configure this file as you see fit or <a href="https://gist.github.com/chsxf/eacf40b47ab4b62de11b2c11e742755e">download this template</a> with my own preferences.</p>

<p>Then enable “format on save” by adding this in the <code class="language-plaintext highlighter-rouge">settings.json</code> file:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"editor.formatOnSave"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></code></pre></div></div>

<h2 id="ignoring-some-files">Ignoring Some Files</h2>

<p>If you’re using <a href="https://git-scm.com/">git</a> to save your work (<em>And I hope you do use either git or any other of its competitors!</em>), there are some files you need to ignore. When building your game, many intermediary files will be created that should not be committed to your repository.</p>

<p>If you have no <code class="language-plaintext highlighter-rouge">.gitignore</code> file in your repository (many hosting providers offer initial templates when creating a repository), create one at the root of your project.</p>

<p>Then, either way, add these lines to your <code class="language-plaintext highlighter-rouge">.gitignore</code> file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>build
*.pdx
.DS_Store
</code></pre></div></div>

<h1 id="conclusion">Conclusion</h1>

<p>You’re now ready to work with the PlayDate Simulator and create great games and apps for the PlayDate. I’ll update this tutorial in due time with instructions on how to build, run, and debug on the actual device.</p>]]></content><author><name>Christophe SAUVEUR</name></author><category term="PlayDate" /><summary type="html"><![CDATA[Spritekit isn't going away, but I wanted to see how difficult it would be to developer games for the PlayDate in C. Turns out, not many resources exist to do that with Visual Studio Code. In this post, I explain how to set up Visual Studio Code to build, run, and debug for the PlayDate Simulator.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/14/playdate-w-vscode.png" /><media:content medium="image" url="https://chsxf.dev/assets/posts/14/playdate-w-vscode.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Working With Fonts in SpriteKit</title><link href="https://chsxf.dev/2023/08/27/13-working-with-fonts-in-spritekit.html" rel="alternate" type="text/html" title="Working With Fonts in SpriteKit" /><published>2023-08-27T00:00:00+00:00</published><updated>2023-08-28T00:00:00+00:00</updated><id>https://chsxf.dev/2023/08/27/13-working-with-fonts-in-spritekit</id><content type="html" xml:base="https://chsxf.dev/2023/08/27/13-working-with-fonts-in-spritekit.html"><![CDATA[<p><img src="/assets/posts/13/fonts-spritekit.png" alt="Fonts with SpriteKit" /></p>

<p>Documentation for <a href="https://developer.apple.com/spritekit/">SpriteKit</a> is sparse. Many parts of the framework are pretty obscure. So that’s no surprise if many solutions can be found outside of Apple on personal blogs, or <a href="https://stackoverflow.com/questions/tagged/sprite-kit">StackOverflow</a> and the likes. However, there are some very specific areas that are even more obscure, with pretty much no information available out there. That’s the case for fonts.</p>

<h1 id="the-simple-way">The Simple Way</h1>

<p>If you are using the <a href="/2023/02/11/9-when-xcode-meets-spritekit.html">SpriteKit Scene Editor</a> embedded in Xcode, this post won’t interest you much. Fonts are handled pretty easily like on any other <a href="https://en.wikipedia.org/wiki/WYSIWYG">WYSIWYG</a> editor. You choose your font, with its specific style and weight, and it appears inside your <a href="https://developer.apple.com/documentation/spritekit/sklabelnode">SKLabelNode</a> as expected.</p>

<p>The only thing you may need to do if you use a custom font that is not pre-installed with macOS is to bundle it with your app and <a href="https://developer.apple.com/documentation/bundleresources/information_property_list/atsapplicationfontspath">declare</a> <a href="https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app">its usage</a> to the system. But beyond that, you’re good to go.</p>

<h1 id="a-family-matter">A Family Matter</h1>

<p>Unfortunately, that’s not the same story if you want to handle fonts programmatically. To identify the font to use on a specific SKLabelNode, SpriteKit provides you with a single property only: <a href="https://developer.apple.com/documentation/spritekit/sklabelnode/1520129-fontname"><code class="language-plaintext highlighter-rouge">fontName</code></a>.</p>

<p>This cryptic field hides in fact a lot of information. But first we have to take a step back and understand how fonts actually work.</p>

<p>Fonts are divided into families. Each family has a different name. Some popular font families are for example <strong>Arial</strong> or <strong>Times New Roman</strong>. Then, in a given family, you have variants for each style (italic, oblique, condensed, etc) and weight (light, bold, regular, etc). Not all font families contain all style and weight variants though. And to make matters worse there is no standardization when it comes to variant names.</p>

<p>For example, the italic version of Times New Roman is called <em>TimesNewRomanPS-ItalicMT</em>. Whereas it is <em>Arial-ItalicMT</em> for Arial. Or <em>Baskerville-Italic</em> for the Baskerville font family. There are similarities, but this is not 100% consistent.</p>

<h1 id="ask-the-oracle">Ask the Oracle</h1>

<p>If it wasn’t confusing enough, SKLabelNode’s <code class="language-plaintext highlighter-rouge">fontName</code> property accepts two possible values: a font family (ie <code class="language-plaintext highlighter-rouge">Times New Roman</code>) or a font variant name (ie <code class="language-plaintext highlighter-rouge">TimesNewRomanPS-ItalicMT</code>). If you set the property with a font family, the default regular font variant will be used. But how to guess the font variant name then if you want to use italic or bold, or even both?</p>

<p>If you are using only a few fonts, you can get that information from the <strong>Font Book</strong> app and use it directly into your code. For that, select a font variant and display its information. In the right panel, you should be given access to an <strong>Identifier</strong> section (see screenshot below).</p>

<p><img src="/assets/posts/13/font-book-identifier.png" alt="Font Book app showing a font variant name" />
<em>Font Book app showing a font variant name</em></p>

<p>However, in my case, <a href="https://github.com/chsxf/CiderKit">CiderKit</a> is an engine. It should be able to handle any font. If you’re facing a similar issue, that means you have to query the system’s font management thanks to either <a href="https://developer.apple.com/documentation/appkit/nsfontmanager/"><code class="language-plaintext highlighter-rouge">NSFontManager</code></a> on macOS or <a href="https://developer.apple.com/documentation/uikit/uifontdescriptor/"><code class="language-plaintext highlighter-rouge">UIFontDescriptor</code></a> anywhere else.</p>

<p>At the end of this post, there is a Swift script you can use to get the right font variant name for SpriteKit.</p>

<p>You can use it like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">myLabel</span> <span class="o">=</span> <span class="kt">SKLabelNode</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="s">"My text string"</span><span class="p">)</span>
<span class="c1">// ...</span>
<span class="n">myLabel</span><span class="o">.</span><span class="n">fontName</span> <span class="o">=</span> <span class="kt">FontHelpers</span><span class="o">.</span><span class="nf">fontName</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="s">"Times New Roman"</span><span class="p">,</span> <span class="nv">italic</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">bold</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
</code></pre></div></div>

<p>This script is currently limited to regular, italic, bold, or bold italic variants, but should be pretty easy to extend if you need to.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if os(macOS)</span>
<span class="kd">import</span> <span class="kt">AppKit</span>
<span class="cp">#else</span>
<span class="kd">import</span> <span class="kt">UIKit</span>
<span class="cp">#endif</span>

<span class="kd">final</span> <span class="kd">class</span> <span class="kt">FontHelpers</span> <span class="p">{</span>

    <span class="kd">class</span> <span class="kd">func</span> <span class="nf">fontName</span><span class="p">(</span><span class="n">with</span> <span class="nv">fontFamily</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">italic</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">,</span> <span class="nv">bold</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="nf">getFontName</span><span class="p">(</span><span class="n">fontFamily</span><span class="p">,</span> <span class="n">italic</span><span class="p">,</span> <span class="n">bold</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="cp">#if os(macOS)</span>

    <span class="kd">fileprivate</span> <span class="kd">class</span> <span class="kd">func</span> <span class="nf">getFontName</span><span class="p">(</span><span class="n">_</span> <span class="nv">fontFamily</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="n">_</span> <span class="nv">italic</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">,</span> <span class="n">_</span> <span class="nv">bold</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">traits</span><span class="p">:</span> <span class="kt">NSFontTraitMask</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="n">italic</span> <span class="p">{</span>
            <span class="n">traits</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="o">.</span><span class="n">italicFontMask</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="n">bold</span> <span class="p">{</span>
            <span class="n">traits</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="o">.</span><span class="n">boldFontMask</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">let</span> <span class="nv">font</span> <span class="o">=</span> <span class="kt">NSFontManager</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">font</span><span class="p">(</span><span class="nv">withFamily</span><span class="p">:</span> <span class="n">fontFamily</span><span class="p">,</span> <span class="nv">traits</span><span class="p">:</span> <span class="n">traits</span><span class="p">,</span> <span class="nv">weight</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="nv">size</span><span class="p">:</span> <span class="mi">12</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">font</span><span class="p">?</span><span class="o">.</span><span class="n">fontName</span>
    <span class="p">}</span>

    <span class="cp">#else</span>

    <span class="kd">fileprivate</span> <span class="kd">class</span> <span class="kd">func</span> <span class="nf">getFontName</span><span class="p">(</span><span class="n">_</span> <span class="nv">fontFamily</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="n">_</span> <span class="nv">italic</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">,</span> <span class="n">_</span> <span class="nv">bold</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">descriptor</span> <span class="o">=</span> <span class="kt">UIFontDescriptor</span><span class="p">()</span><span class="o">.</span><span class="nf">withFamily</span><span class="p">(</span><span class="n">fontFamily</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">bold</span> <span class="o">||</span> <span class="n">italic</span> <span class="p">{</span>
            <span class="k">var</span> <span class="nv">traits</span><span class="p">:</span> <span class="kt">UIFontDescriptor</span><span class="o">.</span><span class="kt">SymbolicTraits</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">if</span> <span class="n">italic</span> <span class="p">{</span>
                <span class="n">traits</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="o">.</span><span class="n">traitItalic</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="n">bold</span> <span class="p">{</span>
                <span class="n">traits</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="o">.</span><span class="n">traitBold</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">newDescriptor</span> <span class="o">=</span> <span class="n">descriptor</span><span class="o">.</span><span class="nf">withSymbolicTraits</span><span class="p">(</span><span class="n">traits</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">descriptor</span> <span class="o">=</span> <span class="n">newDescriptor</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">let</span> <span class="nv">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="p">(</span><span class="nv">descriptor</span><span class="p">:</span> <span class="n">descriptor</span><span class="p">,</span> <span class="nv">size</span><span class="p">:</span> <span class="mi">12</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">font</span><span class="o">.</span><span class="n">fontName</span>
    <span class="p">}</span>

    <span class="cp">#endif</span>

<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Christophe SAUVEUR</name></author><category term="SpriteKit" /><category term="Fonts" /><summary type="html"><![CDATA[Documentation for SpriteKit is sparse. Many parts of the framework are pretty obscure, with pretty much no information available out there. That’s the case for fonts. In this post, I explain how to work programmatically with fonts in SpriteKit.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/13/fonts-spritekit.png" /><media:content medium="image" url="https://chsxf.dev/assets/posts/13/fonts-spritekit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Building My Own CSS Parser</title><link href="https://chsxf.dev/2023/05/06/12-building-own-css-parser.html" rel="alternate" type="text/html" title="Building My Own CSS Parser" /><published>2023-05-06T00:00:00+00:00</published><updated>2023-05-06T00:00:00+00:00</updated><id>https://chsxf.dev/2023/05/06/12-building-own-css-parser</id><content type="html" xml:base="https://chsxf.dev/2023/05/06/12-building-own-css-parser.html"><![CDATA[<p>Now rendering is at a stable stage (at least stable enough), I’ve started working on the next chapter of <a href="https://github.com/chsxf/CiderKit">CiderKit</a>: User Interfaces. And that’s a big one! User input is can be handled out of the box by <a href="https://developer.apple.com/spritekit/">SpriteKit</a>. However, as far as I know, there is no proper UI toolkit available for SpriteKit. Providing basic UI components like containers, buttons, or even sliders, should be relatively easy. But styling them efficiently can be somewhat challenging. Fortunately, the web solved most of this issue many years ago with Cascading Style Sheets (CSS).</p>

<p><img src="/assets/posts/12/Cascading_Style_Sheets.jpg" alt="Cascading Style Sheets" /></p>

<p>CSS have evolved a lot since their introduction in 1996, going through several monolithic iterations up to version 2.1, then being split in many different independent modules for CSS 3 and 4. From a specification point of view, this is bit of a mess. I only need a subset of it though: no flexbox, no positioning.</p>

<h1 id="why">Why?</h1>

<p>Because embarking in writing my own CSS parser, I did some research. Unfortunately, to the best of my abilities, I didn’t find something that really suited my needs. <a href="https://scinfu.github.io/SwiftSoup/">SwiftSoup</a> is a full HTML parser, way too complex for my use case. And <a href="https://github.com/search?q=swift%20css%20parser&amp;type=repositories">others</a> are either not maintained or not Swift packages (and I don’t want to invest anymore in Cocoapods or Carthage). Furthermore, I will probably limit styling to the actual look of the controls and don’t need many of the positioning features of CSS.</p>

<h1 id="whats-in-it-whats-missing">What’s In It? What’s Missing?</h1>

<p>And then <a href="https://github.com/chsxf/CiderCSSKit">CiderCSSKit</a> was born. For now, it’s mainly a pure lightweight CSS parser.</p>

<p>Here’s the list of existing or missing features:</p>

<ul>
  <li>syntax validation is implemented, but you can create any property with any value you like, without control (<code class="language-plaintext highlighter-rouge">background-image: 10px</code> for example)</li>
  <li>provides easy access to style properties, in bulk or individually</li>
  <li>named colors are already implemented, but any other keyword will be interpreted as a string</li>
  <li>complex CSS combinators (<code class="language-plaintext highlighter-rouge">&gt;</code>, <code class="language-plaintext highlighter-rouge">+</code>, and <code class="language-plaintext highlighter-rouge">~</code>) are not implemented</li>
  <li>no support for pseudo-classes and pseudo-elements (like <code class="language-plaintext highlighter-rouge">:hover</code>, <code class="language-plaintext highlighter-rouge">:first-child</code>, or <code class="language-plaintext highlighter-rouge">::first-line</code> for example)</li>
  <li>no support for attribute selectors (like <code class="language-plaintext highlighter-rouge">a[target]</code> or <code class="language-plaintext highlighter-rouge">a[target="_blank"]</code>)</li>
  <li>built-in functions are limited to <code class="language-plaintext highlighter-rouge">rgb</code> and <code class="language-plaintext highlighter-rouge">rgba</code></li>
  <li>short hexadecimal colors (<code class="language-plaintext highlighter-rouge">#fff</code> for example) are not supported (colors must use six hexadecimal digits)</li>
</ul>

<h1 id="whats-next">What’s Next?</h1>

<p>I plan to implement ways to extend the capabilities of the parser, notably with a specific model for custom validation and functions. That will allow the use of CiderCSSKit in other contexts than CiderKit’s UI management. I hope that in the future, the package will be agnostic enough to be use by anyone out there. But for now, I have to test it in real situations and improve.</p>

<p><em><a href="https://commons.wikimedia.org/wiki/File:Cascading_Style_Sheets.jpg">Header image</a> by <a href="https://negativespace.co/">Negative Space</a> under <a href="https://creativecommons.org/publicdomain/zero/1.0/deed.en">Creative Commons CC0 1.0</a></em></p>]]></content><author><name>Christophe SAUVEUR</name></author><category term="CiderKit" /><category term="CiderCSSKit" /><summary type="html"><![CDATA[I've started working on the next chapter of CiderKit: User Interfaces. And that's a big one! In this post I explain why I ended up building my own CSS parser in order to apply styles to my UI elements.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/12/Cascading_Style_Sheets.jpg" /><media:content medium="image" url="https://chsxf.dev/assets/posts/12/Cascading_Style_Sheets.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">CiderKit’s Custom Lighting Model</title><link href="https://chsxf.dev/2023/03/19/11-ciderkit-lighting-model.html" rel="alternate" type="text/html" title="CiderKit’s Custom Lighting Model" /><published>2023-03-19T00:00:00+00:00</published><updated>2023-03-19T00:00:00+00:00</updated><id>https://chsxf.dev/2023/03/19/11-ciderkit-lighting-model</id><content type="html" xml:base="https://chsxf.dev/2023/03/19/11-ciderkit-lighting-model.html"><![CDATA[<p><a href="https://developer.apple.com/spritekit/">SpriteKit</a> is a 2D game engine. But, even though it provides a built-in lighting system, its limitations make difficult working with specific designs, like pseudo 3D. Unfortunately, <a href="/2023/01/08/7-a-look-back-at-2022.html">CiderKit</a>, the SpriteKit-based game engine I’m working on, calls for more than what’s available out of the box. So I had to find a solution. Here’s how it went.</p>

<p><img src="/assets/posts/11/spritekit-sample-image.png" alt="SpriteKit Sample Image" />
<em>SpriteKit Sample Image from Apple Developer Website</em></p>

<h1 id="the-objective">The Objective</h1>

<p>In its first iteration, CiderKit is designed to handle isometric maps. SpriteKit provides a <a href="https://developer.apple.com/documentation/spritekit/sktilemapnode">tile system</a> than can produce such maps. But there’s a catch. In order to achieve complex level designs, I want to support various elevations in the same level, though SpriteKit is limited to a single plane. It was the first blow.</p>

<p>I also wanted to achieve great control over the lighting, notably over intensity and precise near and far attenuations. However, SpriteKit <a href="https://developer.apple.com/documentation/spritekit/sklightnode">light nodes</a> are limited, preventing me from providing additional data. And last but not least, SpriteKit lights are by definition 2D lights. Due to the complexity of my target lighting model, I needed true 3D positioning.</p>

<p>In front of these issues, I started designing my own solution. But many challenges were still ahead.</p>

<h1 id="the-plan">The Plan</h1>

<p><img src="/assets/posts/11/lighting-model.png" alt="CiderKit Lighting Model" /></p>

<p>I am not entirely sure how SpriteKit handles lighting, but I’m pretty confident it is using a forward rendering strategy. It implies computing lighting as each sprite is rendered, without any regard (and possible optimization) for overdraw<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Isometric maps with elevation can produce a lot of overdraw, with parts of the level occluding others. This problem can also be wildly exacerbated if you add a lot of visual details. I was looking for a solution that would allow me to use many individual lights, so forward rendering was clearly not the best approach.</p>

<p>I immediately settled for a deferred rendering pipeline. As the name suggests, this kind of rendering method defers the lighting phase at the end of the process, allowing for a single computation per pixel and massively limiting the impact of overdraw. However, it requires several preparatory render passes to gather the necessary information like position, orientation, and albedo. But how to do that within the very efficient but rigid update loop of SpriteKit? I would have to find out.</p>

<p>As I stated earlier, SpriteKit lights are not suitable in my case. The second part of my plan then became the implementation of new custom lights. That opened a lot of additional questions. Should they be SpriteKit nodes or only data abstractions? How many can I use at the same time? With how many associated data?</p>

<h1 id="deferring-the-execution">Deferring The Execution</h1>

<p>First step to a deferred rendering pipeline: implementing multiple render passes, each associated with a specific information for the light computation. Each of these render passes produces a projection on screen of the corresponding data, stored in a render texture. This way, the final lighting step will know for each pixel of the screen what information to use.</p>

<p>In any given 3D space, in order to shade a scene, in complement of all data from the lights, you need at least three information: position, orientation, and albedo (the “base color” of the material). In a regular 3D pipeline, you would then get 3 render textures: depth, world-aligned normals, and albedo.</p>

<p>With a perspective camera, you can reverse the depth computation in order to obtain the position in space for each pixel. But, with an orthographic camera like the one in CiderKit, depth is not enough. That’s why I had to exchange depth for a true 3D position render texture.</p>

<p>Unfortunately, SpriteKit does not explicitly allow additional render passes or using <a href="https://en.wikipedia.org/wiki/Multiple_Render_Targets">multiple render targets</a>. The engine is voluntarilly limited to preserve its extreme efficiency. I had to find my way through the various APIs and dig out the <a href="https://developer.apple.com/documentation/spritekit/skview/1520114-texture">hidden gem</a> I needed: <code class="language-plaintext highlighter-rouge">SKView.texture(from:)</code></p>

<p>The figure below illustrates the successive steps of the process:</p>

<p><img src="/assets/posts/11/deferred-render-pipeline.drawio.png" alt="CiderKit's Deferred Rendering Pipeline" /></p>

<p>The first four steps are done in the <code class="language-plaintext highlighter-rouge">didFinishUpdate(for:)</code> method of an <a href="https://developer.apple.com/documentation/spritekit/skscenedelegate/">SKSceneDelegate</a>, so after animations and physics have been updated themselves.</p>

<ol>
  <li>First, there is the warmup sequence. I have to do some global shader preparation. And I disable the objects that should not be considered for the light render passes.</li>
  <li>Then, I configure the <a href="https://github.com/chsxf/CiderKit/blob/7b2326e6a1608733e239a2fa07127b5986f6ba59/CiderKit-Engine/Resources/Shaders/UberShader.fsh">custom shaders</a> to generate normal data and render the included objects into a new texture object.</li>
  <li>The same is done for positions with a second pass.</li>
  <li>In the cooldown sequence, the custom shaders are then reverted to their default state, in order to produce albedo data. I also gather light data for the lighting shader that will be applied later. Finally, I reactivate all objects.</li>
</ol>

<p>The three last steps are handled by the normal SpriteKit render process. Lit objects are rendererd independently of the overlays thanks to this specific scene node layout:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SKScene
  - Underlays container
      - Grid
      - ...
  - SKEffectNode (Lit objects root - holds the final light gathering shader)
      - Map
      - Other lit objects
      - ...
  - Overlays container
      - Gizmos
      - ...
</code></pre></div></div>

<ul>
  <li>All active objects but the overlays are rendererd first. Any object that does not use my custom shader will render with the default SpriteKit shader.</li>
  <li>Thanks to the <a href="https://developer.apple.com/documentation/spritekit/skeffectnode">SKEffectNode</a> and <a href="https://github.com/chsxf/CiderKit/blob/7b2326e6a1608733e239a2fa07127b5986f6ba59/CiderKit-Engine/Resources/Shaders/LightModelFinalGathering.fsh">the lighting shader</a>, the final gathering stage can then produce actual lighting on the scene.</li>
  <li>Overlay elements can then be rendered normally over the lit objects.</li>
</ul>

<h1 id="mimicking-3d">Mimicking 3D</h1>

<p>As beautifully complex as this deferred render pipeline can be, it does not provide actual data from the sprites. By themselves, 2D sprites are only that: 2D. For the human eye, they can represent a stylized character, a 3D object, or many other things. But they remain a projection of something on a two-dimensional plane. There is no actual data, notably for orientation or relative position, baked into them. So, I have to provide all of it as supplements.</p>

<p>That’s why lit objects in CiderKit are based on three different textures as illustrated below with the default map blocking textures.</p>

<p><img src="/assets/posts/11/data-maps.png" alt="Data Maps" /></p>

<ul>
  <li>The albedo map is pretty straightforward. The texture provides the base color (albedo) directly.</li>
  <li>The position map is probably the most complex one. For each pixel, the texture provides a normalized position information. This normalized data is then translated into the world thanks to specific attributes set during the render process. X, Y and Z values are respectively mapped from the red (R), green (G) and blue (B) channels of the texture.</li>
  <li>The normal map is also relatively simple to understand and works almost like in any 3D software, and stores orientation data for each pixel. Each color channel is mapped to an axis. The sole difference is that the normals are aligned with the isometric world instead of locally with the object. That’s notably why, in this example, each sprite representing one face of the blocking is only represented with a single color as they are already aligned with the corresponding axis of the world.</li>
</ul>

<h1 id="metal-frame-capture">Metal Frame Capture</h1>

<p>Adjusting and fixing all the render passes was not an easy tasks and there were many bumps on the road. By chance, all of this has not been done blindfolded. Thanks to the Metal Frame Capture tool integrated with Xcode, I was able to understand exactly where things went sideways and react accordingly. This is a very powerful tool I encourage you to dig into if you’re working with Metal-capable devices.</p>

<p><img src="/assets/posts/11/metal-frame-capture.png" alt="Metal Frame Capture Graph" />
<em>Metal Frame Capture Graph</em></p>

<h2 id="troubleshooting-availability-of-the-metal-frame-capture-tool">Troubleshooting Availability of the Metal Frame Capture Tool</h2>

<p>However, if you’re still running an Intel Mac, you may have issues accessing the Metal Frame Capture tool. For some reason, Xcode does not always give you access to it. If this is the case for you, you can force its activation from the scheme editor. Go to <strong>Product &gt; Scheme &gt; Edit Scheme…</strong>, then select the <strong>Run</strong> scheme and its <strong>Options</strong> tab. You should have a setting called <strong>GPU Frame Capture</strong>. If not already, set it to <strong>Metal</strong> and the Metal Frame Capture tool should now be available.</p>

<p><img src="/assets/posts/11/metal-frame-capture-setting.png" alt="GPU Frame Capture Setting" /></p>

<h1 id="the-result">The Result</h1>

<p>I still have to actually make games out of CiderKit. However, in my humble opinion, visual tests turned out great so far (as you can see on the following video). There are some limitations, like the fact that I am currently limited to 16 concurrent lights during the render phase, but I’m confident all of this won’t be for nothing.</p>

<script>
    if (!document.youtubeScriptAdded) {
        document.youtubeIds = [];
        document.youtubeScriptAdded = true;

        var tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        function onYouTubeIframeAPIReady() {
            for (var videoId of document.youtubeIds) {
                var playerId = `player_${videoId}`;
                var playerElement = document.getElementById(playerId);
                var playerWidth = playerElement.offsetWidth;
                var playerHeight = Math.ceil(playerWidth / 16 * 9);

                var player = new YT.Player(`player_${videoId}`, {
                    height: playerHeight,
                    width: playerWidth,
                    videoId: videoId,
                    playerVars: {
                        autoplay: 0,
                        modestbranding: 1,
                        rel: 0
                    }
                });
            }
        }
    }

    document.youtubeIds.push("l1EcdVUHVZc");
</script>

<div id="player_l1EcdVUHVZc" class="youtube-video-player"></div>

<p>A great art direction with good light control is critical nowadays for your games to stand out of the very populated crowd. This is a first step, won’t be the last, but an important one nonetheless.</p>

<p><em>Stay tuned for more!</em></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Wherever a sprite is rendered on screen, each of its pixels are computed and drawn. If another opaque sprite is rendered later over the same area of the screen, pixels are computed and drawn once more, potentially invalidating previous computations and wasting time. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Christophe SAUVEUR</name></author><category term="CiderKit" /><category term="SpriteKit" /><summary type="html"><![CDATA[SpriteKit is a 2D game engine, but its limitations make difficult working with specific designs, like pseudo 3D. Unfortunately, CiderKit, the SpriteKit-based game engine I'm working on, calls for more than what's available out of the box. In this post, I will discuss how I've implemented my own custom deferred lighting model within the constraints of SpriteKit.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/11/lighting-model.png" /><media:content medium="image" url="https://chsxf.dev/assets/posts/11/lighting-model.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">A macOS Gaming Market Share Solution</title><link href="https://chsxf.dev/2023/03/12/10-macos-gaming-market-share-solution.html" rel="alternate" type="text/html" title="A macOS Gaming Market Share Solution" /><published>2023-03-12T00:00:00+00:00</published><updated>2023-03-12T00:00:00+00:00</updated><id>https://chsxf.dev/2023/03/12/10-macos-gaming-market-share-solution</id><content type="html" xml:base="https://chsxf.dev/2023/03/12/10-macos-gaming-market-share-solution.html"><![CDATA[<p><img src="/assets/posts/10/mac-gaming.png" alt="Header Image" /></p>

<p>In its early days, Apple (and subsequently the Mac) was a big player in the gaming market. Many important titles (like <a href="https://en.wikipedia.org/wiki/Myst">Myst</a>) and influential studios (like <a href="https://en.wikipedia.org/wiki/Bungie">Bungie</a>) were born in the Apple ecosystem of yesteryears. But as time went by, due to many factors, the global Apple market share in the personal computer segment plumeted. And so went its relevance with gaming, as Richard Moss noted in his <a href="https://www.bitmapbooks.com/collections/all-books/products/the-secret-history-of-mac-gaming-expanded-edition">Secret History of Mac Gaming</a>.</p>

<p><img src="/assets/posts/10/secret-history-of-mac-gaming.png" alt="The Secret History of Mac Gaming" />
<em>The Secret History of Mac Gaming, by Richard Moss / Bitmap Books</em></p>

<p>Nowadays, macOS’s market share remains very low. In gaming, it is about 2.5% of the installed base according to the <a href="https://store.steampowered.com/hwsurvey">Steam Hardware Survey</a>. That’s pretty much nothing. And yet, most storefronts like <a href="https://store.steampowered.com/">Steam</a>, <a href="https://www.gog.com/">GOG</a> or the <a href="https://store.epicgames.com/">Epic Games Store</a> have compatible offerings, with many indie titles but almost no big AA or AAA games.</p>

<p>In 2021, I wrote a piece on Medium about the state of <a href="https://chsxf.medium.com/gaming-on-a-mac-in-2021-e59bcc7c94eb">Mac gaming in 2021</a>. Even now that Apple Silicon Macs are there and in their second generation, everything I wrote then remains relevant. Macs were already machines on which you could play. Now that Apple Silicon brought back performance, they are even better gaming machines. So, what’s the problem?</p>

<h1 id="the-offering">The Offering</h1>

<p>The problem is the lack of games. Apple with macOS is pretty much on the same spot Epic was when they launched their Epic Games Store. Confronted with the Steam defacto monopoly, Epic had to find a solution. Their store is still not relevant or profitable for most developers, but they are gradually grabbing some chunks of the market. All of that thanks to exclusivity deals and weekly free games.</p>

<p><img src="/assets/posts/10/epic-steam.png" alt="Epic vs Steam" />
<em>Illustration by Alex Castro / The Verge</em></p>

<p>The parallel with macOS is there too, Steam being replaced by Windows. Microsoft’s operating system can basically play any game. Why on earth would someone buy a Mac to play only a fraction of the gaming catalog? At a substantially higher price even. Current Mac gamers are mostly people that also use their Mac for something else, making gaming one of the cherries on top. Many Mac users who can afford it also have a Windows gaming PC on the side.</p>

<p>Back on the Epic case. Why would you buy a game on the Epic Games Store if you are already a client on Steam? It is less convenient to have two launchers and your collection of games is divided. But all it takes is a free account. The entry barrier is way lower than with console exclusive titles (as you have to buy a $500 console). And even lower if you consider the price of a very powerful Mac, like those equiped with the Max or Ultra chips.</p>

<h1 id="resident-evil-village">Resident Evil Village</h1>

<p>At WWDC 2022, Apple made <a href="https://twitter.com/chsxf/status/1534278771976441856">several gaming oriented announcements</a> that gave us hope for a brighter future. In the <a href="https://www.youtube.com/live/q5D55G7Ejs8?t=4870">same keynote</a>, they notably announced that Resident Evil Village would be ported to macOS as a showcase of the power of Apple Silicon.</p>

<p><img src="/assets/posts/10/resident-evil-village.jpeg" alt="Resident Evil Village on macOS" /></p>

<p>And with its Octobre 28th 2022 release<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, <a href="https://apps.apple.com/app/resident-evil-village/id1640627334">Resident Evil Village</a> delivered! Not everything was perfect, but reviews of the port were <a href="https://www.youtube.com/watch?v=-_OkjC_xJR4">generally</a> <a href="https://www.youtube.com/watch?v=6iXx9lfe62w">positive</a>. It proved Macs are very capable gaming machines, probably at the expense of Apple paying for the port to be done (even though we have no information of a potential deal of this nature).</p>

<p>But once again, there’s a catch. Resident Evil Village was released a year before on Windows and consoles. This is basically a technical demo at this point. Mac players were thrilled. But they were alone. The same story goes with the ports of Divinity - Original Sin 2. And in the future with No Man’s Sky. All these games are great, but they are not exclusives. There’s not a chance it will improve significantly macOS’ market share.</p>

<h1 id="apple-arcade">Apple Arcade</h1>

<p>With <a href="https://www.apple.com/apple-arcade/">Arcade</a>, Apple tried to appeal to gamers with exclusive titles. However, for how good these games were, they weren’t significant enough to bring a wide outsider audience to the service. Pretty much none of them were widely expected by players and thus they remained dispensable. These games made Apple Arcade a very good deal for a time, but I don’t expect anyone to actually buy a Mac to play Arcade games, especially as all of them are also available on iOS/iPadOS.</p>

<p><img src="/assets/posts/10/apple-arcade.jpg" alt="Apple Arcade" /></p>

<p>That brings the question of the interest of Apple for gaming on the Mac. As the iPhone and the iPad are huge sellers in the gaming segment (not AAA gaming for sure, but they generate a lot of money anyway), is the Mac still relevant with gaming for Apple in their ecosystem? I obviously do not have the answer, but I know for sure that many people, myself included, would be glad to be able to play more games on their Macs.</p>

<h1 id="the-alternative">The Alternative</h1>

<p>Recently, <a href="https://support.microsoft.com/en-us/windows/options-for-using-windows-11-with-mac-computers-with-apple-m1-and-m2-chips-cd15fd62-9b34-4b78-b0bc-121baa3c568c">Microsoft announced</a> that they will officially support Windows for ARM on Apple Silicon Macs through <a href="https://www.parallels.com">Parallels</a>. That’s a great news for users with mixed workflows as BootCamp, the official Apple solution to run Windows natively on Intel Macs, is not available anymore on Apple Silicon.</p>

<p><img src="/assets/posts/10/parallels.jpg" alt="Parallels running Windows and macOS virtual machines on a macOS host" /></p>

<p>However, even if this is a very efficient solution for existing Mac customers to enjoy their Mac even more, that does not resolve the main issue of the availability of native games and the overall attractiveness of the platform for outsiders. Plus, Parallels (or <a href="https://www.vmware.com/products/fusion.html">VMWare Fusion</a>) is not free. It’s at least $100 to add to the bill.</p>

<p>With Parallels, Apple is pretty much in the exact same situation than linux gaming with Proton and the Steam Deck. The Steam Deck is so good at running most Windows games from the Steam catalog that developers do not have to bother porting their games to linux (even though the Deck runs SteamOS, a derivative of Debian, a linux distribution). Parallels won’t incentize developers to bring native ports of their games to the platform.</p>

<p>And to add insult to injury, you also have many cloud streaming services available for macOS, such as <a href="https://www.nvidia.com/en-us/geforce-now/">GeForce Now</a>, <a href="https://boosteroid.com/">Boosteroid</a>, <a href="https://www.xbox.com/en-US/cloud-gaming?xr=shellnav">Xbox Cloud Gaming</a>, …</p>

<h1 id="the-solutions">The Solutions</h1>

<p>Then, what to do? The list of potential solutions is not long in my opinion. If Apple intends to increase the market share of the Mac, pro users won’t help much more. Indeed, the profesional and creative audiences are already there. The main issues, namely performance and lack of connectivity, have been addressed respectively with Apple Silicon and the various Mac redesigns. Now, it is the consumer market that needs to be addressed. And games are a huge part of it.</p>

<p>Commissioning ports of important AAA games is good from a technological stand point. But the timing is not relevant. Releasing Mac ports several months or years after the initial release won’t improve the situation. The player base is already constituted elsewhere. At least ports need to exist from day one.</p>

<p>Another true game changer (pun intended) could be exclusive titles, timed or not, coming from franchise that have an already installed audience. If Apple could get several big names, early adopters of such games would be forced to buy a Mac. Is this a good practice? No. As a PlayStation player, I was so disappointed when Microsoft got Rise of the Tomb Raider exclusive to Xbox consoles for a year. I even considered buying a Xbox One. I didn’t in the end as I was already too busy to play all the games in my back catalog, so I waited. However, some players were converted this way. In the same vein, remember when Microsoft snatched Halo, which was originally planned as a cross-platform Windows+MacOS game?</p>

<h1 id="now-what">Now What?</h1>

<p>The “gaming on macOS” situation is rather complex. The market share won’t increase by magic. Apple has to do something but it is difficult to know what exactly. Technologically, the future is bright. Now bring on the games!</p>

<p><em><a href="https://www.trustedreviews.com/wp-content/uploads/sites/54/2023/01/Mac-Mini-M2-4-920x517.jpg">Header image</a> by <a href="https://www.trustedreviews.com">Trusted Reviews</a> under <a href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons 4.0</a></em></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>To my readers from France: Resident Evil Village has been <a href="https://iphoneaddict.fr/post/news-356341-resident-evil-village-disparait-temporairement-mac-app-store-france">temporarilly removed from the french Mac App Store</a> last january. There is not information on when it should be available again. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Christophe SAUVEUR</name></author><category term="macOS" /><category term="Gaming" /><summary type="html"><![CDATA[In its early days, Apple (and subsequently the Mac) was a big player in the gaming market. But as time went by, the global Apple market share in the personal computer segment plumeted. And so went its relevance with gaming. In this post, I discuss the issues Apple is facing in the desktop gaming market and potential solutions that in my humble opinion could improve the situation.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/10/mac-gaming.png" /><media:content medium="image" url="https://chsxf.dev/assets/posts/10/mac-gaming.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">When Xcode Meets SpriteKit</title><link href="https://chsxf.dev/2023/02/11/9-when-xcode-meets-spritekit.html" rel="alternate" type="text/html" title="When Xcode Meets SpriteKit" /><published>2023-02-11T00:00:00+00:00</published><updated>2023-08-27T00:00:00+00:00</updated><id>https://chsxf.dev/2023/02/11/9-when-xcode-meets-spritekit</id><content type="html" xml:base="https://chsxf.dev/2023/02/11/9-when-xcode-meets-spritekit.html"><![CDATA[<p><img src="/assets/posts/9/xcode-spritekit.png" alt="Xcode + Spritekit" /></p>

<p>I expect most macOS, iOS, or tvOS developers to use <a href="https://developer.apple.com/xcode/">Xcode</a> as their main development environment, especially if they’re working with Swift. You can use other IDEs with other languages, like <a href="https://dotnet.microsoft.com/en-us/apps/xamarin">Xamarin</a> or <a href="https://unity.com/">Unity</a> for example, but that’s not the “Apple way”. This obviously also applies to anyone working with <a href="https://developer.apple.com/spritekit/">SpriteKit</a>. In this post, I will detail what’s good or wrong with Xcode support for SpriteKit. And there’s much to say!</p>

<h1 id="available-tools-in-xcode">Available Tools in Xcode</h1>

<p>As for many file types and other frameworks like SceneKit, Xcode comes preinstalled with some built-in editors for SpriteKit. The goal is to help authoring SpriteKit projects visually, pretty much like you would edit your scenes in the Unity editor for example. Here are some of the most useful.</p>

<h2 id="the-scene-editor">The Scene Editor</h2>

<p>With the SpriteKit Scene Editor, you can basically build and animate your scenes visually directly from Xcode. I’ve used it for <a href="https://github.com/chsxf/SKTetris">SKTetris</a> and it can be pretty functional. You can add to the scene most of the built-in nodes like sprites, labels, shapes, or even tile maps. Your scenes are then stored in <code class="language-plaintext highlighter-rouge">.sks</code> files that you can load directly by name from your app’s bundle.</p>

<p><img src="/assets/posts/9/spritekit-scene-editor.png" alt="SpriteKit Scene Editor" /></p>

<p>It is even possible to setup GameplayKit’s entities and components from the Scene Editor. Each type of node comes with its own custom inspector that allows you to control their look and feel. Animation is another part of the Scene Editor. Indeed, the bottom part of the editor allows you to create custom animations, based on <a href="https://developer.apple.com/documentation/spritekit/skaction">SKAction</a>, for your various nodes. Finally, SpriteKit scenes being nodes themselves, you can easily create isolated scenes for some elements that you want to reuse or load inside another scene. It is also a very good tool to create UI layers for your SceneKit project, as you can use a SpriteKit overlay if needed.</p>

<h2 id="the-asset-catalog">The Asset Catalog</h2>

<p>Pretty much no scene would be whole without some sprites, right? That’s where the Asset Catalog may come handy. Indeed, it is the place to store your sprites, individually, or eventually ask Xcode to combine them into atlases for optimum performance. You can even store them in different resolutions for different screen sizes if needed. The Asset Catalog will also handle texture compression or hardware classes (like memory or GPU configurations) to help you provide the most adequate texture to any device in the whole Apple line-up.</p>

<p><img src="/assets/posts/9/asset-catalog.png" alt="Asset Catalog" /></p>

<h2 id="the-tileset-editor">The TileSet Editor</h2>

<p>As its name suggests, the TileSet Editor is designed to manage sets of tiles for various map types. You can configure tile groups for complex 2D, isometric, or hexagonal grids. The tile sets, groups and rules are then directly usable in the Scene Editor with a Tile Map node. The TileSet Editor works in tandem with the Asset Catalog as the sprites have to be part of a catalog before being used in tile groups.</p>

<p><img src="/assets/posts/9/tileset-editor.png" alt="TileSet Editor" /></p>

<h2 id="the-particle-system-editor">The Particle System Editor</h2>

<p>Almost as self-explanatory is the Particle System Editor which helps setting up particle systems visually, that can be then used inside the Scene Editor or programmatically afterwards. Many parameters of the particles can be tweaked, from the lifespan, the speed, the scale, the rotation, or the color over time.</p>

<p><img src="/assets/posts/9/particle-system.png" alt="Particle System Editor" /></p>

<h1 id="not-every-love-story-ends-well">Not Every Love Story Ends Well</h1>

<p>Unfortunately, for almost every good thing Xcode brings to the table, there are drawbacks and inconveniences that make them pretty much unusable in practice.</p>

<h2 id="the-trusty-asset-catalogs-and-tilesets">The Trusty Asset Catalogs and TileSets</h2>

<p>The Asset Catalogs and TileSets are probably the most valuable assets in everything Xcode has to offer. First, they are properly serialized with JSON files and folders so it is very easy to work with, even in external tools. And the fact that they are humanly readable make resolving conflicts much easier when working with versioning in teams.</p>

<p>However, not everything is perfect. Asset Catalogs notably misses some key parameters like the filtering mode (all textures are created with linear filtering), constraining you to handle nearest filtering mode through code when you use it, notably in pixel art games.</p>

<p>Having more control over the compression format would be great too. <strong>Automatic</strong> or <strong>Lossless</strong> is not enough, and allowing access to the wide variety of formats handled by Metal would be nice. For example, <a href="https://medium.com/@chsxf/unity-the-untold-story-of-16-bits-textures-e94a9408d795">16-bit pixel formats</a> can be very useful to reduce application size without sacrificing quality in many cases.</p>

<p>One last thing is that we have no control over the generated atlases. There is no export of the actual coordinates of the sprites, making almost impossible for additional data layers (normals, emissive, …) to match the exact position for their own sprites.</p>

<h2 id="double-trouble-scenes">Double Trouble Scenes</h2>

<p>The Scene Editor is very useful to visualize your scenes. However, don’t try to work in teams on complex scenes (or on particle systems, whose files share the same issues). <code class="language-plaintext highlighter-rouge">.sks</code> files are generated as binary files. If two people work on the same file at the same time, you are pretty much out of luck if you have to resolve conflicts. Unless you convert them to XML. Because, under the hood, <code class="language-plaintext highlighter-rouge">.sks</code> files are just binary property list files with another name.</p>

<p>With this simple command-line, you can convert them to XML:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plutil <span class="nt">-convert</span> xml1 <span class="nt">-o</span> my-xml-scene.sks my-binary-scene.sks
</code></pre></div></div>

<p>I tried a few years ago to convert <code class="language-plaintext highlighter-rouge">.sks</code> files to XML to see if Xcode would open them. But at the time, it was immediately crashing. This problem seems now to be resolved as of Xcode 14.2 but if you modify your scene file and save it again, it will fall back to binary. That’s pretty inconvenient. There may be a solution to explore with git’s pre-commit hooks, but Xcode should allow one way or another to keep <code class="language-plaintext highlighter-rouge">.sks</code> files as XML files. Even settings the file type as XML in the file inspector doesn’t help.</p>

<p>Another broader issue I have with the SpriteKit Scene Editor is the lack of customization. The editor allows you to assign GameplayKit’s components to nodes in the scene, and you can declare some properties as inspectable that will appear in the user data section (thanks to the <code class="language-plaintext highlighter-rouge">@GKInspectable</code> attribute). However, this does not work well for custom types or even enumerations. And you can’t add validators or custom fields like you would in Unity’s custom inspectors.</p>

<p>Both of these issues are huge hits for productivity as they can become regular annoyances or total blockers.</p>

<h1 id="the-impact-on-my-personal-workflow">The Impact on My Personal Workflow</h1>

<p>When I started working on CiderKit, I comtemplated my options. But everything mentioned above geared me towards finding custom solutions. Furthermore, the built-in tilemap node for isometric maps is fairly limited compared to what I planed: there’s no elevation support.</p>

<p>Also, even if I still use the Asset Catalogs for storing my textures, atlases themselves are created in an external tool beforehand (at the time directly with <a href="https://www.aseprite.org/">Aseprite</a>, but most likely thanks to <a href="https://www.codeandweb.com/texturepacker">TexturePacker</a> as the pipeline will mature). Sprites are then generated programmatically based on custom JSON files describing the atlas structure.</p>

<p>Obviously, all of this requires a lot of investment in time and effort to bypass the weaknesses of Xcode’s own implementations. But that also gives me a lot of control regarding data management and optimization, producing way better results in the end.</p>

<p>This is not a surprise that Apple does not invest a lot in SpriteKit in regards to the small community of developers behind it. I am even grateful that SpriteKit has not been deprecated, and I am firmly crossing my fingers it won’t happend anytime soon. But the stack that runs under the hood is very efficient, if not perfect, and we can produce great things with it anyway.</p>

<p>The tools are not everything. The people wielding them are important too.</p>]]></content><author><name>Christophe SAUVEUR</name></author><category term="Xcode" /><category term="SpriteKit" /><summary type="html"><![CDATA[I expect most macOS, iOS, or tvOS developers to use Xcode as their main development environment, especially if they're working with Swift. You can use other IDEs with other languages, like Xamarin or Unity for example, but that's not the "Apple way". This obviously also applies to anyone working with SpriteKit. In this post, I will detail what's good or wrong with Xcode support for SpriteKit.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/9/xcode-spritekit.png" /><media:content medium="image" url="https://chsxf.dev/assets/posts/9/xcode-spritekit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">My 2023 Roadmap</title><link href="https://chsxf.dev/2023/01/13/8-my-2023-roadmap.html" rel="alternate" type="text/html" title="My 2023 Roadmap" /><published>2023-01-13T00:00:00+00:00</published><updated>2023-01-13T00:00:00+00:00</updated><id>https://chsxf.dev/2023/01/13/8-my-2023-roadmap</id><content type="html" xml:base="https://chsxf.dev/2023/01/13/8-my-2023-roadmap.html"><![CDATA[<p><img src="/assets/posts/8/2023-goals.jpg" alt="2023 Goals" /></p>

<p>This post is a direct follow-up to my <a href="/2023/01/08/7-a-look-back-at-2022.html">2022 retrospective</a>. This time I will lay out my goals for the new year, regarding my projects and this website.</p>

<h1 id="post-more-often">Post More Often</h1>

<p>Six posts in 2022 is not nothing. But in my opinion, it’s not enough. I’m not aiming for quantity over quality though. However, I should be able to publish more often without sacrificing content. Good regular content probably requires planning and that’s exactly what I will do. I already have six other posts in the pipeline and hope to publish once every two weeks going forward.</p>

<p>A few examples:</p>

<ul>
  <li>Lighting with CiderKit</li>
  <li>A Formal Introduction to MFX</li>
  <li>Work-Life Balance is not Binary</li>
</ul>

<h1 id="updating-my-icloud-implementation">Updating My iCloud Implementation</h1>

<p>Almost since its initial release, <a href="https://nihongonokana.com">Nihongo no Kana</a> has always supported iCloud synchronization accross devices. However, the current implementation uses deprecated features and I have yet to find a replacement, as no explicit upgrade path is provided by Apple. This is the least likely update to happen this year as it will require a lot of research, trial and error while implementing, and testing to ensure a consistent experience to the users. I have to admit that I’m not looking forward to this task.</p>

<h1 id="releasing-mfx-10">Releasing MFX 1.0</h1>

<p>Last year, I froze the <a href="https://github.com/chsxf/mfx">source code of MFX</a> while planning a 1.0 release. However, a big part of the <a href="https://github.com/chsxf/mfx/wiki/Framework-Reference">documentation</a> is still missing. My plan is to complete the missing pages and proceed with a formal release before the end of the year. This is doable, and MFX can make a very good tool for writing fast and efficient PHP websites or even APIs.</p>

<h1 id="ciderkit">CiderKit</h1>

<p>A lot of things happened with <a href="https://github.com/chsxf/CiderKit">CiderKit</a> last year, and a lot will happen this year. However, <strong>The Untitled Project</strong> remains mostly undefined at this point and will probably end up being a multi-year journey.</p>

<p>How can I be sure I’m moving in the right direction? I can’t if I’m waiting for the final release. And that would be stupid. So my plan is to release smaller incremental games, for which I build tools that can be reused accross projects, expanding the scope of CiderKit progressively.</p>

<p>My main focus this year will be around UI, as the first of these smaller games should not need much more on the sprite assets side, now that I have animation tools at hand. Expect news and screenshots all year round, and maybe a release at the beginning of 2024.</p>

<h1 id="six-months-from-now">Six Months From Now</h1>

<p>The start of the summer holidays, at the beginning of July here in France, will be a good point in time to check on these goals, to see how many have been reached and how many won’t be fulfilled.</p>

<p><em>So stay tuned!</em></p>

<p><em><a href="https://www.flickr.com/photos/91261194@N06/52492217121">Header image</a> by <a href="https://www.flickr.com/people/91261194@N06/">Jernej Furman</a> under <a href="https://creativecommons.org/licenses/by/2.0/">Creative Commons 2.0</a></em></p>]]></content><author><name>Christophe SAUVEUR</name></author><category term="Blog" /><summary type="html"><![CDATA[In this direct follow-up to my 2022 retrospective post, I will lay out my goals for the new year, regarding my projects and this website.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chsxf.dev/assets/posts/8/2023-goals.jpg" /><media:content medium="image" url="https://chsxf.dev/assets/posts/8/2023-goals.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>