<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Massimog's massiblog]]></title><description><![CDATA[Videogames and things that are unrelated to Videogames (also Diorama Break news).]]></description><link>https://blog.massimogauthier.com</link><image><url>https://substackcdn.com/image/fetch/$s_!WlVN!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc58c93dc-201b-41d2-b463-6960be30646a_208x208.png</url><title>Massimog&apos;s massiblog</title><link>https://blog.massimogauthier.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 08 Apr 2026 23:08:15 GMT</lastBuildDate><atom:link href="https://blog.massimogauthier.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Massimo Gauthier]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[massimog@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[massimog@substack.com]]></itunes:email><itunes:name><![CDATA[Massimo Gauthier]]></itunes:name></itunes:owner><itunes:author><![CDATA[Massimo Gauthier]]></itunes:author><googleplay:owner><![CDATA[massimog@substack.com]]></googleplay:owner><googleplay:email><![CDATA[massimog@substack.com]]></googleplay:email><googleplay:author><![CDATA[Massimo Gauthier]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The Diorama Break Chapter 1 Demo is Out Now on Steam!]]></title><description><![CDATA[It&#8217;s been a long month (and a half), but I&#8217;m happy to be able to say that it&#8217;s finally out of Beta and available on Steam!]]></description><link>https://blog.massimogauthier.com/p/the-diorama-break-chapter-1-demo-ddd</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/the-diorama-break-chapter-1-demo-ddd</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Tue, 07 Apr 2026 17:01:22 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7618049f-9e4b-4347-b021-078031f9cfc2_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s been a long month (and a half), but I&#8217;m happy to be able to say that it&#8217;s finally out of Beta and available on Steam! Check it out: <a href="https://store.steampowered.com/app/4519970/Diorama_Break_Demo">https://store.steampowered.com/app/4519970/Diorama_Break_Demo</a><br></p><p>If you&#8217;re still not in the loop, I&#8217;ve also got a shiny new trailer to go along with the occasion! </p><div id="youtube2-jNYVRdt0850" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;jNYVRdt0850&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/jNYVRdt0850?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><p><br>A huge thank you to everyone who played and left feedback on the beta! Many of the improvements since February would likely not have happened without you. While I&#8217;m sad I wasn&#8217;t able to get to everyone&#8217;s feedback (especially for more recent respondents), you&#8217;ve all given me a lot to think about for the full game.</p><p>While there&#8217;s still a ton that could be improved with this demo, I&#8217;ve committed to not letting the &#8220;good enough&#8221; be the enemy of the &#8220;totally shippable bro&#8221;. With that said, and even if I&#8217;ll probably still be working on this demo for weeks to come, this is nevertheless a version I&#8217;m truly proud of and ready to share far and wide.</p><p>And speaking of the full game, I can finally confirm that <strong>we&#8217;ll be launching a Kickstarter to help fund it on April 28th</strong>! If you enjoyed the demo and want to see the full project realized, keep an eye out for that and tell your friends! Really. We&#8217;re still pretty underground, so sharing the demo around is liable to have a big impact.</p><p>You can check out the pre-launch page here: <a href="https://www.kickstarter.com/projects/massimog/diorama-break">https://www.kickstarter.com/projects/massimog/diorama-break</a></p>]]></content:encoded></item><item><title><![CDATA[The Diorama Break Chapter 1 Demo is out now in OPEN BETA!]]></title><description><![CDATA[Hey everyone!]]></description><link>https://blog.massimogauthier.com/p/the-diorama-break-chapter-1-demo</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/the-diorama-break-chapter-1-demo</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Sat, 21 Feb 2026 19:33:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WlVN!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc58c93dc-201b-41d2-b463-6960be30646a_208x208.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey everyone! Anyone who&#8217;s been following over the past year should be aware that I&#8217;ve been working on a special little Tactics JRPG. Today, I&#8217;m happy to announce that the Chapter 1 Demo for that game is now available in <strong>OPEN BETA </strong>on Itch.io: <a href="https://massimog.itch.io/dioramabreak">https://massimog.itch.io/dioramabreak</a></p><p>While this version of the demo is fully playable from start to finish, some polish, balance tweaks, and quality of life features are still forthcoming (particularly audio improvements). Once these planned changes and player feedback has been implemented, a full demo release is planned for Steam sometime in March!</p><p>Look forward to it!</p>]]></content:encoded></item><item><title><![CDATA[PAX West 2025 Retrospective]]></title><description><![CDATA[Back home after presenting Diorama Break at PAX West; the show was a big success, launching us from complete unknowns to almost 1000 wishlists on Steam!]]></description><link>https://blog.massimogauthier.com/p/pax-west-2025-retrospective</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/pax-west-2025-retrospective</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Thu, 04 Sep 2025 09:01:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/17ffe4f3-99c2-4789-a48e-01ed4d09b7f3_201x251.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Back home after presenting <a href="https://www.dioramabreak.com">Diorama Break</a> at PAX West; the show was a big success, launching us from complete unknowns to almost 1000 wishlists on Steam! To celebrate our new followers, I thought I&#8217;d write up a quick retrospective while the trip is still fresh in my mind, for those curious about what something like this looks like from the developer&#8217;s POV.</p><h2>The City</h2><p>I was pleasantly surprised that I was able to get to my hotel from the airport in just under an hour using Seattle&#8217;s light rail system. Coverage and frequency could be better, but the trains and stations felt pleasant and clean by North American standards. I get the sense that this system has spent what little resources it&#8217;s been given into hitting a decent standard for travelers (specifically, those staying in the city proper), which makes sense.</p><p>As is typical in NA though, things rapidly deteriorate once you get away from the city center. I went to a wrap party held at a venue about 40 minutes away, thankfully was still able to get there by train but the 15 minute walk between the station and the restaurant was along a gigantic stroad and has got to be one of the creepiest I&#8217;ve ever taken.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c0qC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c0qC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 424w, https://substackcdn.com/image/fetch/$s_!c0qC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 848w, https://substackcdn.com/image/fetch/$s_!c0qC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 1272w, https://substackcdn.com/image/fetch/$s_!c0qC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c0qC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png" width="1456" height="1013" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1013,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8439382,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/172748437?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c0qC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 424w, https://substackcdn.com/image/fetch/$s_!c0qC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 848w, https://substackcdn.com/image/fetch/$s_!c0qC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 1272w, https://substackcdn.com/image/fetch/$s_!c0qC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad8f377f-5702-4a8d-8d86-38d3bab99e51_3102x2159.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Didn&#8217;t snap a pic, but imagine walking from here to the yellow sign in the very back, at night, mostly deserted.</figcaption></figure></div><p>Google maps also lied to me about when the trains would stop running, which was fun to discover after having walked back to the station at 1AM&#8230;</p><p>The architecture was somewhat more interesting than average, though nothing breathtaking or too out of the norm for NA. Density in the city center felt like the densest parts of my home city Montreal, but over a noticeably wider area. Good, but it didn&#8217;t really blow me away like New York did. Roads were an interesting mix of very wide (three car lanes + bus lane) and quite narrow (enough to cross in two strides; I felt safe ignoring the traffic light).</p><h2>The People</h2><p>One thing I always notice visiting America is how willing total strangers on the street are to randomly say things to you or even strike up a conversation. It&#8217;s a surprising fact, since as much as people like to say Canada isn&#8217;t all that different I&#8217;ve almost never had that happen here. I&#8217;d say I&#8217;m somewhat indifferent to this over my usual status quo, it can be a fun diversion but sometimes you don&#8217;t really have the energy for it. Related to this, though this seems like more of a west coast thing: the front-facing service workers I met almost all either projected a sense that they wanted to kill me or that they are utterly <em>jazzed </em>to be talking to me<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. Both vibes are somewhat uncomfortable, but likely just because I&#8217;m used to a more &#8220;genial yet straightforward and curt&#8221; attitude.</p><p>Another thing that stood out: in discussions, I was struck by how desperate people<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> seemed to maintain social harmony, or, more accurately, a mutual sense that no one was offended or hurt by anything done or said by anyone<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>. The most notable example was when mentioning to a group that I did not drink alcohol, all three people I was talking to <em>visibly </em>sprang to offer me a laundry list of reasons why that was completely fine in their eyes. When I asked them why they and others seemed to do things like that, one put forth the idea that it was due to people wanting to foster unity despite the area&#8217;s cosmopolitanism. Coming from a fairly cosmopolitan city myself, this explanation seemed lacking, though maybe it&#8217;s just a skill issue on our part. The same person claimed it was a mixed blessing though, since in his experience many would act familiar to the point where it seemed they wanted to pursue friendship, yet cancel plans and refuse to ever follow up later.</p><h2>The Food</h2><p>Was ok. The state of American takeout food always feels perfectly congruent with the litany of health crises the country is experiencing<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>. I did have some nice thai food, and the hotel I was staying at had okay-ish vegan pizza next door. Tried some salty ice cream; novel, but not something I&#8217;m likely to do again. I&#8217;ll admit I did not have the time or money to try anything top rated, so don&#8217;t take this as criticism of the best Seattle has to offer.</p><h2>The Expo</h2><p>I wish I could say the actual experience of exhibiting was fun, but the build wasn&#8217;t exactly finished when we were due to start<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a>, so the first three days ended up very stressful and very sleepless (advice to my past self: don&#8217;t underestimate how long it will take to complete the demo by more than half, you fool, you moron<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a>). The last two days were fortunately less stressful thanks to a more stable build, but as usual for these sorts of events, marred by exhaustion. I didn&#8217;t even get to try Kirby Air Riders<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a>&#8230;</p><p>We were also only two (huge, huge thanks to <a href="https://www.bezeltorpe.net/">Rowan</a>, one of the artists on the game, for flying down and helping out), which is pushing it; three feels like the minimum needed to not have someone crippled by exhaustion on the last day (again, huge, huge thanks to Rowan). Important advice: get shoes optimized for standing. I brought two pairs, my normal shoes for the trip and a pair of Hokas for the actual expo. Sit down frequently, especially if you&#8217;re in pain; don&#8217;t try to push through it. Rest properly at the end of every day.</p><p>Still, through it all, it was an incredible relief to see the very (dare I say it, overwhelmingly) positive reaction to the game. With how many unorthodox choices I made in the writing and game design, it was immensely gratifying to me to have people praise them directly, and the rest of the team were all stoked to receive so many compliments on the game&#8217;s visuals and music. And all this despite how much content was missing from the demo we showed<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-8" href="#footnote-8" target="_self">8</a>! I can&#8217;t wait to put the completed demo out and show you guys what we can really pull off.</p><p>And beyond just compliments, it&#8217;s nice to have finally created something that people cared enough about to give, uh, constructive feedback on (some of it valuable, even!). We even got recommended some games to learn from (hadn&#8217;t heard of Phantom Brigade, but I&#8217;ll definitely be <s>stealing</s> tastefully adopting some UI ideas from that game). </p><p>Everything went smoothly with the event staff and organizers, no complaints there. Though I&#8217;ll say, it must feel vaguely infantilizing for the attendees to have the enforcers form a human wall at the end of every day and sweep through the convention like a wave, bright flashing lights and all. I got the sense though that this was more a teambulding exercise for the staff rather than something deemed strictly necessary.</p><p>Also had some <a href="https://store.steampowered.com/app/2514330/The_Rabbit_Haul/">great booth neighbours</a>; was pleasant to talk to an out-of-province Canadian dev about the various things, especially the programs and services available to them, both public and private.</p><h2>Wrap-up</h2><p>With everything over and done with, it&#8217;s important to recover properly. For that and no other reason, I&#8217;ll be taking the next couple days to rest, then it&#8217;s back to the grind. Enjoy Silksong everyone!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Though in both cases, I got the distinct sense that I was someone to be Handled. Probably fair, given what these people likely have to deal with.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>I don&#8217;t want to generalize too much here, this probably just applies to &#8220;Nerdy Seattleites&#8221;</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>&#8220;You&#8217;re good&#8221; seems a common response to apologies to minor mistakes. As someone used to &#8220;it&#8217;s ok/fine&#8221; this is quite notable in a way that&#8217;s difficult to put my finger on. I think it&#8217;s because the latter only communicates a fact about the world, so the former comes across as jarringly personal in comparison. </p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Yet another thing that surprisingly differs so much from Quebec. I think it&#8217;s probably due to <em>much </em>more liberal use of cooking oil.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>To give you an idea, <em>all </em>of the audio was implemented in-game the night before the show.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p>I blame Antonblast. I still have no idea how we (just 4 people at the time) were able to put together <em>that </em>kickstarter demo in just 4 months.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p>You guys don&#8217;t understand, he has <em>maximum pink.</em></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-8" href="#footnote-anchor-8" class="footnote-number" contenteditable="false" target="_self">8</a><div class="footnote-content"><p>I swear, every missing interaction and UI glitch felt like a gut punch&#8230; wasn&#8217;t exactly helping my mood&#8230;</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Diorama Break Reveal]]></title><description><![CDATA[It&#8217;s finally time.]]></description><link>https://blog.massimogauthier.com/p/diorama-break-reveal</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/diorama-break-reveal</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Fri, 15 Aug 2025 21:56:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/rGioZdM1MGY" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s finally time. It&#8217;s I-don&#8217;t-want-to-say-how-many months behind schedule, but I&#8217;m finally ready to show a peek of what I&#8217;ve been working on since Antonblast came out:</p><div id="youtube2-rGioZdM1MGY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;rGioZdM1MGY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/rGioZdM1MGY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Waow! Wondering where this will go? Maybe, uuuuuuuuh&#8230; subscribe? </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p><p>Me and my team are working as fast as we can to finish up a proper demo within the next couple months (as the ship that is my personal funds buckles and collapses beneath us); I&#8217;ll definitely have more to share here then (or earlier, if I feel like it). And as the project progresses, I&#8217;ll aim to post regular updates about whatever it is we&#8217;ve been working on lately, if it turns out there&#8217;s an audience for it.</p><p>In the meantime though, why not join the new <a href="https://discord.gg/4gVZXqVHmA">discord</a> or <a href="https://store.steampowered.com/app/3932580/Diorama_Break">wishlist the game on steam</a>? Who knows, maybe you&#8217;ll find some more info about the project there&#8230;</p><p>If you just can&#8217;t wait to play it though, we&#8217;re planning to have the first half of the planned demo playable at PAX West in just under a month! <a href="https://west.paxsite.com/en-us/expo-hall/arch-expo-hall/showroom.html?gtID=628821&amp;exhibitor-name=Studio-Massimo-Gauthier-inc">Come check us out in the Arch Expo Hall</a>.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Seeing how well an agentic AI coding tool can do compared to me using an actual real-world example]]></title><description><![CDATA[As you may or may not know, I am in the camp of those concerned about existential risk from AI (don&#8217;t know what that is?]]></description><link>https://blog.massimogauthier.com/p/seeing-how-well-an-agentic-ai-coding</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/seeing-how-well-an-agentic-ai-coding</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Sun, 01 Jun 2025 16:39:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BY6f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As you may or may not know, I am in the camp of those concerned about existential risk from AI (don&#8217;t know what that is? There are plenty of good explainers out there, from the more <a href="https://youtu.be/0bnxF9YfyFI?si=jnWlbnFeAVVsOvat">accessible</a> to the more <a href="https://www.lesswrong.com/posts/uMQ3cqWDPHhjtiesc/agi-ruin-a-list-of-lethalities">advanced</a>). As part of that ongoing interest in not seeing me and everything I love melted down for raw materials, I try keep up with developments in AI capabilities. Recently, though it has little to do with <em>that </em>giant world-ending problem, I stumbled across a more mundane dispute that overlaps with my other interests: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BY6f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BY6f!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 424w, https://substackcdn.com/image/fetch/$s_!BY6f!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 848w, https://substackcdn.com/image/fetch/$s_!BY6f!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 1272w, https://substackcdn.com/image/fetch/$s_!BY6f!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BY6f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png" width="887" height="494" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:494,&quot;width&quot;:887,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!BY6f!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 424w, https://substackcdn.com/image/fetch/$s_!BY6f!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 848w, https://substackcdn.com/image/fetch/$s_!BY6f!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 1272w, https://substackcdn.com/image/fetch/$s_!BY6f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d97181b-4a76-43cc-9b7d-a59bee8105f2_887x494.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rEuF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rEuF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 424w, https://substackcdn.com/image/fetch/$s_!rEuF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 848w, https://substackcdn.com/image/fetch/$s_!rEuF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 1272w, https://substackcdn.com/image/fetch/$s_!rEuF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rEuF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png" width="905" height="459" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:459,&quot;width&quot;:905,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:83077,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684b7c85-f891-4c8e-af9c-7fbc2ce818ba_905x462.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rEuF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 424w, https://substackcdn.com/image/fetch/$s_!rEuF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 848w, https://substackcdn.com/image/fetch/$s_!rEuF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 1272w, https://substackcdn.com/image/fetch/$s_!rEuF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ceb7026-d0bf-404e-b12f-d49db1b08204_905x459.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a sense, I think this Stokes guy is right. Capabilities are improving at tremendous speed, anyone like me expecting AI to get to the point where it can conquer the world (or, more optimistically, all jobs) should naturally expect to see it master software development sometime before then. But the point of contention <em>here </em>is whether current AI systems can even provide significant productivity gains to programmers working on meaningful, complex systems and&#8230; ha&#8230; haha&#8230; yeah, I&#8217;m very much on Blow&#8217;s side on that one.</p><p>But, y&#8217;know, anyone can just toss that opinion out there. Fortunately for me though, it doesn&#8217;t seem like many skilled programmers that hold the &#8220;current AI is incompetent&#8221; view have actually bothered to substantiate their point (at least, with an article like this). I feel like, when I see these sorts of disputes, a lot of people are missing context.<em>Everyone </em>agrees the AI isn&#8217;t perfect, but what exactly is the AI failing at and why does it seem to matter so much more to some people? As someone who has tried AI out in multiple contexts, I thought it would be valuable to give the &#8220;strongest&#8221; tools a fair shot and closely scrutinize and critique the actual outputs. Who knows, maybe I&#8217;ll get a benchmark named after me<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>.</p><h1>The Task</h1><p>The task is simple, and so are the rules:</p><ul><li><p>I have recently added a simple camera shake feature to the game I&#8217;m working on. This was done without any AI assistance. Based on the edit history of the relevant files, this took me about 30 minutes.</p></li><li><p>I will branch off from just before I added the feature, then use an AI tool to recreate it. My goal will not be to necessarily perfectly replicate my implementation, but to prompt the AI and make edits until it&#8217;s version of the feature is basically functional. I will then critique the code it provided throughout this process.</p></li><li><p>If the AI tool and I cannot achieve basic functionality (from when I start typing the first prompt for the AI), I will critique what work it <em>was </em>able to do inside of 30 minutes.</p></li></ul><p>Frankly, I&#8217;m throwing the AI a bit of a softball here. The problem is not too difficult, we&#8217;re not doing any major overhauls to the program&#8217;s structure, we&#8217;re not adding a feature that touches multiple systems, and most of the structure is already in place, no need to add any entirely new systems. This is partly to keep this test easy to run, partly to keep things short and understandable for beginners, and partly as an additional sneaky test for the AI: can it recognize that there&#8217;s no need to overcomplicate things?</p><h2>My implementation</h2><p>Before we give the AI a shot, let&#8217;s take a look at my existing implementation to get an idea of where our standards are at.</p><p>As mentioned, the goal was to create a camera shake system. Specifically, I wanted a function I could call which:</p><ul><li><p>Takes a duration (in frames) and a force (in pixels) and shakes the camera that much, for that long.</p></li><li><p>Calling the function multiple times should create an <em>additive </em>effect, so multiple sources of camera shake can add up to create a stronger overall effect.</p></li><li><p>I&#8217;d also like the ability to limit the shake to only the x or y axis.</p></li></ul><p>Before I started, the camera was mainly controlled by the stage system, and its final position primarily depended on three global vector2 variables:</p><ul><li><p><strong>stage.target_camera_pos</strong>: Primarily meant to be messed around with by entities in the stage. After setting this value, the camera&#8217;s <em>center position </em>will (later in the frame, when the camera updates) be moved to this value. At the moment, the camera will always snap instantly to this position, but in the future I may have it move more smoothly/gradually towards it.</p></li><li><p><strong>stage.camera_pos</strong>:<strong> </strong>The camera&#8217;s actual position in the stage. If you imagine the camera&#8217;s view as a rectangle, this position would be in the top left.</p></li><li><p><strong>camera.pos</strong>:<strong> </strong>When the game draws anything, this value is applied such that the camera system works as intended. This value is extremely volatile and is mainly intended to be a cached value that can be quickly accessed and used in drawing functions without needing to cast or modify the value. When changing drawing contexts (such as when drawing to a separate texture), a new camera position is pushed onto a stack, and is popped off said stack when returning to the previous drawing context. Whenever the stack changes, this value is updated to reflect the top value of the stack. Also corresponds to the camera&#8217;s top-left position. <strong>stage.camera_pos</strong> gets pushed onto the base of the stack when the current stage begins rendering. </p></li></ul><p>Here&#8217;s what the stage system update (which was basically just camera code) looked like:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!htCe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!htCe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 424w, https://substackcdn.com/image/fetch/$s_!htCe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 848w, https://substackcdn.com/image/fetch/$s_!htCe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 1272w, https://substackcdn.com/image/fetch/$s_!htCe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!htCe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png" width="1409" height="332" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:332,&quot;width&quot;:1409,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:70231,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!htCe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 424w, https://substackcdn.com/image/fetch/$s_!htCe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 848w, https://substackcdn.com/image/fetch/$s_!htCe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 1272w, https://substackcdn.com/image/fetch/$s_!htCe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10321551-d0dc-4de6-a09e-b6b8902e977a_1409x332.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>In summary:</p><ul><li><p>If the camera is tracking a specific entity, set the target position accordingly.</p></li><li><p>Set stage.camera_pos based on stage.target_camera_pos</p></li><li><p>Clamp stage.camera_pos to the stage bounds.</p></li></ul><p>camera.pos is then set via the provided function in the stage rendering code, right before it starts drawing stuff:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8-2x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8-2x!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 424w, https://substackcdn.com/image/fetch/$s_!8-2x!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 848w, https://substackcdn.com/image/fetch/$s_!8-2x!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 1272w, https://substackcdn.com/image/fetch/$s_!8-2x!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8-2x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png" width="909" height="263" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:263,&quot;width&quot;:909,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38950,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8-2x!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 424w, https://substackcdn.com/image/fetch/$s_!8-2x!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 848w, https://substackcdn.com/image/fetch/$s_!8-2x!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 1272w, https://substackcdn.com/image/fetch/$s_!8-2x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f3b3dc-9790-4d8f-8398-5cb0f2ed2111_909x263.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p>Alright, with all that preamble out of the way, let&#8217;s look at the changes made. Since I was finally adding per-frame camera-related behavior that arguably wasn&#8217;t also closely tied to the stage system, I first decided to move all the camera code in the stage system to a camera-specific update function:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k4hF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k4hF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 424w, https://substackcdn.com/image/fetch/$s_!k4hF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 848w, https://substackcdn.com/image/fetch/$s_!k4hF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 1272w, https://substackcdn.com/image/fetch/$s_!k4hF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k4hF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png" width="1456" height="267" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:267,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30899,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k4hF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 424w, https://substackcdn.com/image/fetch/$s_!k4hF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 848w, https://substackcdn.com/image/fetch/$s_!k4hF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 1272w, https://substackcdn.com/image/fetch/$s_!k4hF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5e26094-1c75-4ab3-932e-8e3a4f5b02ee_1645x302.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NIpp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NIpp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 424w, https://substackcdn.com/image/fetch/$s_!NIpp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 848w, https://substackcdn.com/image/fetch/$s_!NIpp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 1272w, https://substackcdn.com/image/fetch/$s_!NIpp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NIpp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png" width="1456" height="225" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:225,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!NIpp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 424w, https://substackcdn.com/image/fetch/$s_!NIpp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 848w, https://substackcdn.com/image/fetch/$s_!NIpp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 1272w, https://substackcdn.com/image/fetch/$s_!NIpp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F070fadd0-3b15-42fc-950b-2a8451a8866a_1713x265.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x1LE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x1LE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 424w, https://substackcdn.com/image/fetch/$s_!x1LE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 848w, https://substackcdn.com/image/fetch/$s_!x1LE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 1272w, https://substackcdn.com/image/fetch/$s_!x1LE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!x1LE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png" width="557" height="333" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:333,&quot;width&quot;:557,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22598,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!x1LE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 424w, https://substackcdn.com/image/fetch/$s_!x1LE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 848w, https://substackcdn.com/image/fetch/$s_!x1LE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 1272w, https://substackcdn.com/image/fetch/$s_!x1LE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58c410ff-5820-48e6-8565-e87f2c6c4403_557x333.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That left _stage_system_update empty, but I decided to keep it around in case I want to add anything non-camera related to it later. </p><p>The simplest form of camera shake essentially just involves adding some random value to the camera&#8217;s &#8220;unshaken&#8221; position every frame. With that in mind, and since _camera_system_update is intended to be the &#8220;end point&#8221; for any camera changes in a frame (i.e. any special camera behaviors I add in the future will either be called inside the function or before it), I decided to implement camera shake by modifying stage.camera_pos after we&#8217;ve set it in _camera_system_update. Since I wanted the camera to be able to shake slightly outside the room bounds, I decided to add the shaking code <em>after </em>we clamp stage.camera_pos.</p><p>But I still needed some values to refer to in order to determine how much shaking to apply. Since we want shaking to be able come from different sources, which could shake things for different lengths of time and with different strength, I decided the best way to proceed was to have a pool of shake &#8220;requests&#8221; that can be added to or removed from<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aRk2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aRk2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 424w, https://substackcdn.com/image/fetch/$s_!aRk2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 848w, https://substackcdn.com/image/fetch/$s_!aRk2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 1272w, https://substackcdn.com/image/fetch/$s_!aRk2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aRk2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png" width="915" height="743" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:743,&quot;width&quot;:915,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55326,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aRk2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 424w, https://substackcdn.com/image/fetch/$s_!aRk2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 848w, https://substackcdn.com/image/fetch/$s_!aRk2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 1272w, https://substackcdn.com/image/fetch/$s_!aRk2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202966b8-9cc0-43e4-b50a-8a7277ede8ac_915x743.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each shake request stores a how long it should keep on going for, as well as a force value. The force is stored as a vector2, allowing me to apply different amounts to the x and y axis.</p><p>Exposing this to the &#8220;user&#8221; (me) in the form of the originally specified function is trivial:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!N_JG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N_JG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 424w, https://substackcdn.com/image/fetch/$s_!N_JG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 848w, https://substackcdn.com/image/fetch/$s_!N_JG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 1272w, https://substackcdn.com/image/fetch/$s_!N_JG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!N_JG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png" width="638" height="191" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8f27885-0b32-490f-8679-d57891e05c18_638x191.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:191,&quot;width&quot;:638,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21755,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!N_JG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 424w, https://substackcdn.com/image/fetch/$s_!N_JG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 848w, https://substackcdn.com/image/fetch/$s_!N_JG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 1272w, https://substackcdn.com/image/fetch/$s_!N_JG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8f27885-0b32-490f-8679-d57891e05c18_638x191.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>With all that out of the way, applying the actual shake is simple:</p><ul><li><p>Loop through all the requests, adding up their force, updating their timers and removing them when they run out. The order of the requests doesn&#8217;t matter, so we can use an O(1) time unordered remove.</p></li><li><p>Once you have the total force, for both the x and y axis:</p><ul><li><p>Clamp the force to avoid edge cases where its unnoticeable or overwhelming</p></li><li><p>Apply some randomness to make the camera shake feel more varied</p></li><li><p>Add or subtract (at random) the force value to the camera position </p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oUaF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oUaF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 424w, https://substackcdn.com/image/fetch/$s_!oUaF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 848w, https://substackcdn.com/image/fetch/$s_!oUaF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 1272w, https://substackcdn.com/image/fetch/$s_!oUaF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oUaF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png" width="792" height="601" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b7001212-eb9e-40ad-9210-49eb529385b9_792x601.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:601,&quot;width&quot;:792,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93450,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oUaF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 424w, https://substackcdn.com/image/fetch/$s_!oUaF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 848w, https://substackcdn.com/image/fetch/$s_!oUaF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 1272w, https://substackcdn.com/image/fetch/$s_!oUaF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7001212-eb9e-40ad-9210-49eb529385b9_792x601.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And voila! Now all that was left to do was test and see if it works. The easiest thing to do was just to throw something at the top of the player&#8217;s update loop, so that&#8217;s what I did:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tn1k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tn1k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 424w, https://substackcdn.com/image/fetch/$s_!tn1k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 848w, https://substackcdn.com/image/fetch/$s_!tn1k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 1272w, https://substackcdn.com/image/fetch/$s_!tn1k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tn1k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png" width="450" height="117" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:117,&quot;width&quot;:450,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20493,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tn1k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 424w, https://substackcdn.com/image/fetch/$s_!tn1k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 848w, https://substackcdn.com/image/fetch/$s_!tn1k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 1272w, https://substackcdn.com/image/fetch/$s_!tn1k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ad2d412-3aaf-410e-8683-cfa70331aeae_450x117.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cj5J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cj5J!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!cj5J!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!cj5J!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!cj5J!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cj5J!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:10558168,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cj5J!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!cj5J!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!cj5J!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!cj5J!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb355ed-1593-4d60-96ed-546ad7e93edb_1920x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Waow!</p><p>Here are some combined calls. With this code, the camera shake should increase significantly for 10 frames (1/6th of a second) whenever I click (I also made Pro flinch for effect):</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UTsC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UTsC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 424w, https://substackcdn.com/image/fetch/$s_!UTsC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 848w, https://substackcdn.com/image/fetch/$s_!UTsC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 1272w, https://substackcdn.com/image/fetch/$s_!UTsC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UTsC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png" width="1170" height="235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:235,&quot;width&quot;:1170,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:57718,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UTsC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 424w, https://substackcdn.com/image/fetch/$s_!UTsC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 848w, https://substackcdn.com/image/fetch/$s_!UTsC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 1272w, https://substackcdn.com/image/fetch/$s_!UTsC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae5baef-7ca6-4c63-9eb5-63809a3f34c5_1170x235.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wIOb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wIOb!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!wIOb!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!wIOb!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!wIOb!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wIOb!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13244359,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wIOb!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!wIOb!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!wIOb!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!wIOb!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff674b221-7f5f-408b-9536-d33f50895e27_1920x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Here&#8217;s vertical shake only:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sQUo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sQUo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 424w, https://substackcdn.com/image/fetch/$s_!sQUo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 848w, https://substackcdn.com/image/fetch/$s_!sQUo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 1272w, https://substackcdn.com/image/fetch/$s_!sQUo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sQUo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png" width="1175" height="195" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:195,&quot;width&quot;:1175,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52583,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sQUo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 424w, https://substackcdn.com/image/fetch/$s_!sQUo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 848w, https://substackcdn.com/image/fetch/$s_!sQUo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 1272w, https://substackcdn.com/image/fetch/$s_!sQUo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4098b0a9-5ad2-49b1-a87a-7cf585d02809_1175x195.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Vf5e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Vf5e!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!Vf5e!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!Vf5e!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!Vf5e!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Vf5e!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8145933,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Vf5e!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!Vf5e!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!Vf5e!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!Vf5e!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f2f58cc-30ee-4b93-bad3-518f53232d03_1920x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Tubular!</p><p>And all in just about 30 changed lines!</p><p>Now let&#8217;s see how the AI handles it.</p><h2>The AI&#8217;s implementation</h2><p><em>While I have tried Claude Code in the past, for maximum fairness, I wrote the previous sections before running this test, and won&#8217;t be making any major edits to them after conducting the test.</em></p><p>You&#8217;ve been able to ask AI chatbots to spit out code for you for quite a while now, but more recently firms have begun offering &#8220;agentic&#8221; AI coding tools which can take a look at entire codebases and intelligently make changes. I&#8217;m not interested in comparing a bunch of different tools, so let&#8217;s just go with the best, which right now is&#8230;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mCfX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mCfX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 424w, https://substackcdn.com/image/fetch/$s_!mCfX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 848w, https://substackcdn.com/image/fetch/$s_!mCfX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 1272w, https://substackcdn.com/image/fetch/$s_!mCfX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mCfX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png" width="1000" height="923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:923,&quot;width&quot;:1000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:90130,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mCfX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 424w, https://substackcdn.com/image/fetch/$s_!mCfX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 848w, https://substackcdn.com/image/fetch/$s_!mCfX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 1272w, https://substackcdn.com/image/fetch/$s_!mCfX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e50ba2a-4e41-4931-a42f-66efeb6f3012_1000x923.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://www.vellum.ai/llm-leaderboard">https://www.vellum.ai/llm-leaderboard</a></figcaption></figure></div><p><a href="https://www.anthropic.com/claude-code">Claude Code</a>!</p><p>Setting things up wasn&#8217;t too tough, now let&#8217;s jump right in. You&#8217;ve already seen the initial prompt earlier:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j7Zc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j7Zc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 424w, https://substackcdn.com/image/fetch/$s_!j7Zc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 848w, https://substackcdn.com/image/fetch/$s_!j7Zc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 1272w, https://substackcdn.com/image/fetch/$s_!j7Zc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j7Zc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png" width="1270" height="405" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:405,&quot;width&quot;:1270,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38278,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!j7Zc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 424w, https://substackcdn.com/image/fetch/$s_!j7Zc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 848w, https://substackcdn.com/image/fetch/$s_!j7Zc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 1272w, https://substackcdn.com/image/fetch/$s_!j7Zc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0d4bb45-e93f-452d-8f3c-eaca7c1d1b74_1270x405.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And here&#8217;s the first edit it came up with:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Tq_B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Tq_B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 424w, https://substackcdn.com/image/fetch/$s_!Tq_B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 848w, https://substackcdn.com/image/fetch/$s_!Tq_B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 1272w, https://substackcdn.com/image/fetch/$s_!Tq_B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Tq_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png" width="502" height="551" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:551,&quot;width&quot;:502,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34937,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Tq_B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 424w, https://substackcdn.com/image/fetch/$s_!Tq_B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 848w, https://substackcdn.com/image/fetch/$s_!Tq_B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 1272w, https://substackcdn.com/image/fetch/$s_!Tq_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34ce7585-91c4-4d71-a41e-ad1b6f2d89ef_502x551.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Impressive! The core idea is there, with only a little overcomplication compared to my implementation. Let&#8217;s see if we can correct that:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XUSD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XUSD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 424w, https://substackcdn.com/image/fetch/$s_!XUSD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 848w, https://substackcdn.com/image/fetch/$s_!XUSD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 1272w, https://substackcdn.com/image/fetch/$s_!XUSD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XUSD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png" width="1456" height="481" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:481,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63094,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XUSD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 424w, https://substackcdn.com/image/fetch/$s_!XUSD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 848w, https://substackcdn.com/image/fetch/$s_!XUSD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 1272w, https://substackcdn.com/image/fetch/$s_!XUSD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3273fbd3-6946-4717-a2a8-1cc9d3b7b5ed_1829x604.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Nice.</p><p>It also remembers to init the data structure:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_2Xz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_2Xz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 424w, https://substackcdn.com/image/fetch/$s_!_2Xz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 848w, https://substackcdn.com/image/fetch/$s_!_2Xz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 1272w, https://substackcdn.com/image/fetch/$s_!_2Xz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_2Xz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png" width="457" height="295" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:295,&quot;width&quot;:457,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16932,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_2Xz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 424w, https://substackcdn.com/image/fetch/$s_!_2Xz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 848w, https://substackcdn.com/image/fetch/$s_!_2Xz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 1272w, https://substackcdn.com/image/fetch/$s_!_2Xz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F066de70d-43b0-4dc9-8791-51dc2f6e36df_457x295.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s the main implementation:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0B0A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0B0A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 424w, https://substackcdn.com/image/fetch/$s_!0B0A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 848w, https://substackcdn.com/image/fetch/$s_!0B0A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 1272w, https://substackcdn.com/image/fetch/$s_!0B0A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0B0A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png" width="738" height="960" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:960,&quot;width&quot;:738,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79055,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0B0A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 424w, https://substackcdn.com/image/fetch/$s_!0B0A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 848w, https://substackcdn.com/image/fetch/$s_!0B0A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 1272w, https://substackcdn.com/image/fetch/$s_!0B0A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ffc31fd-f12f-4e78-9d57-2673ca5a7ea8_738x960.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Not bad. Here are the issues though:</p><ul><li><p>The extra camera_shake functions are a nice touch, but given that Odin makes it easy to provide a Vec2 <em>or </em>a scalar to camera_shake, ultimately unnecessary.</p></li><li><p>It&#8217;s keeping a separate global variable for the shake offset. I didn&#8217;t catch that earlier; as you know, it&#8217;s unnecessary.</p></li><li><p>It doesn&#8217;t bother using odin&#8217;s special loop syntax, making the loop definition a teensy bit harder to parse than it needs to be.</p></li><li><p>It uses an ordered_remove to get rid of expired &#8220;shake instances&#8221;, wasting time unnecessarily. Surprising, since it&#8217;s still looping through the instances in reverse.</p></li><li><p>It correctly applies a bit of randomness to the shake, but this is done in an overly wordy way. It also gets the random function&#8217;s name wrong (should be random_f). The exact way randomness is applied is also not quite the same as what I&#8217;m doing, but that&#8217;s not super important and it does result in less calls to the RNG for now. Since this is something I plan to refine later even in my own implementation, I&#8217;ll leave it like this for now.</p></li><li><p>It doesn&#8217;t clamp the shake, but given that I never asked it to do this I&#8217;ll give it a pass for now and come back to that later.</p></li></ul><p>Let&#8217;s give it feedback on the major problems and see where it goes with it:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dCF5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dCF5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 424w, https://substackcdn.com/image/fetch/$s_!dCF5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 848w, https://substackcdn.com/image/fetch/$s_!dCF5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 1272w, https://substackcdn.com/image/fetch/$s_!dCF5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dCF5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png" width="1293" height="919" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:919,&quot;width&quot;:1293,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:91699,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dCF5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 424w, https://substackcdn.com/image/fetch/$s_!dCF5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 848w, https://substackcdn.com/image/fetch/$s_!dCF5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 1272w, https://substackcdn.com/image/fetch/$s_!dCF5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bd37550-0940-4b09-ae76-8fa8389d5ce2_1293x919.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Still a bit more verbose than I&#8217;d like, but let&#8217;s move on.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TRpK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TRpK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 424w, https://substackcdn.com/image/fetch/$s_!TRpK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 848w, https://substackcdn.com/image/fetch/$s_!TRpK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 1272w, https://substackcdn.com/image/fetch/$s_!TRpK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TRpK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png" width="778" height="450" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/91e75468-c443-4015-9949-4826815039ca_778x450.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:450,&quot;width&quot;:778,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38305,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TRpK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 424w, https://substackcdn.com/image/fetch/$s_!TRpK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 848w, https://substackcdn.com/image/fetch/$s_!TRpK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 1272w, https://substackcdn.com/image/fetch/$s_!TRpK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91e75468-c443-4015-9949-4826815039ca_778x450.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Its plan is revealed! This comes before the clamp though, so let&#8217;s fix that, as well as the unnecessary global while we&#8217;re at it:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!exfx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!exfx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 424w, https://substackcdn.com/image/fetch/$s_!exfx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 848w, https://substackcdn.com/image/fetch/$s_!exfx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 1272w, https://substackcdn.com/image/fetch/$s_!exfx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!exfx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png" width="1456" height="1016" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1016,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:211874,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!exfx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 424w, https://substackcdn.com/image/fetch/$s_!exfx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 848w, https://substackcdn.com/image/fetch/$s_!exfx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 1272w, https://substackcdn.com/image/fetch/$s_!exfx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae12dbe4-c119-4694-8356-48584a9bdec9_2297x1603.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cool.</p><p>It then tries to see if the code compiles, but I tell it that I have my own build pipeline and it should just add some test code and let me run it:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2xJe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2xJe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 424w, https://substackcdn.com/image/fetch/$s_!2xJe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 848w, https://substackcdn.com/image/fetch/$s_!2xJe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 1272w, https://substackcdn.com/image/fetch/$s_!2xJe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2xJe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png" width="913" height="383" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:383,&quot;width&quot;:913,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34979,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2xJe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 424w, https://substackcdn.com/image/fetch/$s_!2xJe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 848w, https://substackcdn.com/image/fetch/$s_!2xJe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 1272w, https://substackcdn.com/image/fetch/$s_!2xJe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cd5e57-3c8c-4423-9f77-03b8ef8dfadd_913x383.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Good enough. Testing in-game, it all builds and works fine (Claude has a strange idea of &#8220;gentle&#8221;, but whatever).</p><p>Oh, let&#8217;s not forget:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0epg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0epg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 424w, https://substackcdn.com/image/fetch/$s_!0epg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 848w, https://substackcdn.com/image/fetch/$s_!0epg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 1272w, https://substackcdn.com/image/fetch/$s_!0epg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0epg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png" width="1380" height="809" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:809,&quot;width&quot;:1380,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:81879,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.massimogauthier.com/i/164847546?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0epg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 424w, https://substackcdn.com/image/fetch/$s_!0epg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 848w, https://substackcdn.com/image/fetch/$s_!0epg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 1272w, https://substackcdn.com/image/fetch/$s_!0epg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc20993e7-399b-49b0-a39b-fe9ed7b83189_1380x809.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Wow, that&#8217;s a bit much. Whatever though, also good enough.</p><h1>How&#8217;d it do?</h1><p>Frankly, pretty good! Claude was definitely off the mark at times, but with some scrutiny and feedback, it managed to get pretty close to our &#8220;ideal&#8221; implementation. Sure, its syntax is a little more verbose than necessary, and its architecture is a little less clean (still pretty well segmented though, nothing I&#8217;d consider egregiously wrong). Still, verbosity is a real cost, Claude sometimes taking a dozen lines to express what I can cleanly express in 1-2 can add up to serious problems in terms of readability and maintainability. I can always go clean it up, but man, that&#8217;s gonna add more time, let&#8217;s check and see how long Claude and I were at it-</p><p>Oh. 50 minutes, huh? So much for 5x productivity gains&#8230;</p><p>In the end, I think this test isn&#8217;t going to satisfy either side of the debate very much. Those who believe in the current power of the tools are gonna feel like I spent too long studying and nitpicking every last detail of the AI&#8217;s implementation, squandering any productivity gains. The skeptics, on the other hand, will likely see this task as trivial, and not really proving anything about serious capabilities<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>. I think a more serious version of this would involve running the same process on the implementation of a large and complex system that took multiple days to complete and touched many different parts of the codebase (and didn&#8217;t have as straightforward of an implementation strategy). But that would be far more difficult to fit into a short, accessible post like this one, and I have better things to do<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>!!</p><p>Still, for any confused onlookers willing to really study the details shown here (I do it all for you, my lovelies), I think this exercise will offer you some insight into the current power of these tools. At least until the next big capabilities leap in 6 months completely obsoletes this post.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to receive my new posts via email.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Or more likely just get a bunch of people yelling at me that I&#8217;m not using the AI right.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Since this is a dynamic memory structure, it needs to be initialized alongside the camera system. It exists for the lifetime of the program and we never need to worry about freeing it though, so we can just use the default allocator. &#8220;init&#8221; here is just a proc group I made for initializing data structures in general, it contains a proc that simply calls the built-in make_dynamic_array.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>And also, the fact I had already completed the task myself and knew exactly what to ask for is certainly giving the model a leg-up.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>But maybe I&#8217;ll do a part 2 the next time I use AI to help me build something from scratch. </p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Finale]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-ceb</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-ceb</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Wed, 29 Jan 2025 07:00:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/48314c0b-7002-4b16-9d8f-41ee4526836b_1272x1131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-b4d">&lt;Previous post</a></em></p><div><hr></div><p>Oof, been a while hasn&#8217;t it? It&#8217;s January now and I wrote the previous two posts I just uploaded alongside this one back in August, didn&#8217;t quite have time to put the finishing touches on them before work on Antonblast went crazy, so they languished in my drafts for a bit. Oh yeah, Antonblast, that <a href="https://store.steampowered.com/app/1887400/ANTONBLAST/">came out</a>! I&#8217;d be surprised of anyone reading this hasn&#8217;t seen it already, but in case you didn&#8217;t read the first post in this series, my frustrations working in Game Maker on that project was what set me down this path of making my own engine in the first place. And my opinion hasn&#8217;t exactly turned around, getting that game ready for a Switch release was a nightmare all on its own (one I might write a bit about in a future post). But anyway, it&#8217;s time to put an end to this series, because guess what?</p><p>I did it!! Woo!!!</p><p>That&#8217;s right! As of&#8230; uh&#8230; roughly September-ish, I finally completed my engine! And just in time too, &#8216;cause its first game has officially been in full development since December, just as planned! That&#8217;s been a real timesink, lemme tell ya, but I felt bad not having finished this series, so before my life is <em>entirely </em>consumed by that project, I&#8217;ll run through all the little stuff I missed:</p><h1>Fonts and text rendering</h1><p>Frankly, there&#8217;s a lot more to this topic than I could write about in 100 posts or that I even know, so I&#8217;ll just cover how I handled things for my own purposes. </p><p>When building a text rendering system, you might be tempted, as I was, to just load font (.ttf) files alongside your other assets, then use a library like SDL ttf to read those files and render out your fonts whenever you need to draw text. This was unfortunately a misstep that cost me a few days to fix, because doing things this way is <em>slow. </em>Every time I wanted to draw a new character at a given size, the library would have to generate a texture for that single character using the .ttf file data, which I had to send to the GPU whenever I wanted to draw that single character (see the post on sprites to learn why this isn&#8217;t great). While most libraries will do some caching to help alleviate this, it will still cause unacceptable lag spikes that one doesn&#8217;t have a ton of control over (one frame in testing that had to draw a paragraph of text for the first time all at once took <em>40 whole milliseconds</em> to render, disgusting).</p><p>So, what&#8217;s the alternative? While there are definitely ways to make using .ttf files at runtime faster, if you know ahead of time what fonts you&#8217;ll need and what size you&#8217;ll need them at, it&#8217;s better to just cut out the middle man and pre-generate some font texture pages! Just like sprite texture pages, these are large images that have all the characters of a font rendered onto them and packed tightly together, along with an index file that records where each individual character is located in that image. Rather than including the .ttf file in the final game assets, the build process simply reads those .ttf files, generates these pages, only includes <em>them </em>in the actual game. When rendering text, I can then copy the image data from these textures, and just like sprite pages these have the advantage of being able to batch multiple character draws together before having to send new data to the GPU.</p><p>In order to make this process possible, each .ttf font should have some kind of attached config data that specifies how it gets converted to a font page (I use little .json files stored in the same folder). This data should look something like this:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bAa0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bAa0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 424w, https://substackcdn.com/image/fetch/$s_!bAa0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 848w, https://substackcdn.com/image/fetch/$s_!bAa0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 1272w, https://substackcdn.com/image/fetch/$s_!bAa0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bAa0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png" width="512" height="174" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:174,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11063,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bAa0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 424w, https://substackcdn.com/image/fetch/$s_!bAa0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 848w, https://substackcdn.com/image/fetch/$s_!bAa0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 1272w, https://substackcdn.com/image/fetch/$s_!bAa0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1954b010-2871-41e0-be70-5b7c93a05e41_512x174.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><ul><li><p><strong>Ranges: </strong>Specifies which characters I want to actually have on the font page using <a href="https://jrgraphix.net/research/unicode_blocks.php">unicode ranges</a>. Most games won&#8217;t really need to display e.g. <a href="https://jrgraphix.net/r/Unicode/10380-1039F">the ugaritic alphabet</a>, so it&#8217;s useful to be able to narrow down the characters I need, to save on texture space. The ranges in the example above correspond to the Japanese <a href="https://jrgraphix.net/r/Unicode/3040-309F">hiragana</a> and <a href="https://jrgraphix.net/r/Unicode/30A0-30FF">katakana </a>syllabaries (I&#8217;ve made it so my system always exports the basic latin characters no matter what). If you&#8217;re confused as to why the numbers at the links don&#8217;t match what&#8217;s written in the example, that&#8217;s because unicode indices (&#8216;code points&#8217;) are usually written in <a href="https://simple.wikipedia.org/wiki/Hexadecimal">hexadecimal</a>. If you convert the numbers into decimal, you&#8217;ll see that they match up with the ones I&#8217;ve written.</p></li><li><p><strong>Sizes: </strong>What sizes of the font I want to export. Since I can&#8217;t read the .ttf file on the fly, the texture data I&#8217;m rendering in-game is static, unchanging, so each different size needs to be pre-rendered separately.</p></li><li><p><strong>Page: </strong>Which page to put the font data on. If multiple fonts are often used together, they can be packed together on the same page to make rendering them more efficient.</p></li></ul><h1>Collision</h1><p>This is more of a per-game thing, but I&#8217;ll cover a quick solution for collision that can work in a lot of situations.</p><p>The basic operation of a collision system is checking whether two shapes are overlapping. In the simplest case, two rectangles (call them A and B), this is done by checking 4 things:</p><ul><li><p>If the x position of the left edge of A less than (i.e. to the left of) the x position of the right edge of B</p></li><li><p>If the x position of the right edge of A more than the x position of the left edge of B</p></li><li><p>If the y position of the top edge of A is less than (i.e. above) the bottom edge of B</p></li><li><p>If the y position of the bottom edge of A is more than the y position of the top edge of B</p></li></ul><p>If all 4 of these things are true, it means the rectangles are overlapping (think about it carefully). </p><p>While checking this can be quick, often things get dicey once there are a lot of rectangles to check. Say I&#8217;m a rectangle (maybe I&#8217;m attached to a player) in a stage with 100 other rectangles (maybe they&#8217;re attached to walls, or enemies). To know if I&#8217;m overlapping with any of them, I need to check those 4 conditions 100 times! And I might need to do that several times in one frame (for example, if I&#8217;m moving, say, 8 pixels this frame, then every pixel I move I need to check all 100 rectangles again to know if I&#8217;ve hit a wall and need to stop. In the worst case, that&#8217;s 800 checks). And if any of the <em>other </em>rectangles want to do similar collision checking, they have to go through all the others too! 100 rectangles that want to collide with each other means 10000 collision checks!</p><p>Ok, calling them &#8216;rectangles&#8217; is a bit weird, they&#8217;re usually called &#8216;colliders&#8217;, since they don&#8217;t necessarily have to be rectangles, so I&#8217;ll switch to that, but keep in mind that no matter what we&#8217;re just checking if two shapes overlap.</p><p>Anyway, there are several paths to optimization here, but a simple and easy one is to just narrow down the number of colliders we need to check. If a tiny collider is on one end of a large stage, I know for sure it&#8217;s not going to be colliding with another tiny collider all the way on the other end. Knowing this, I can split the stage up into &#8220;partitions&#8221;, which each has a list of the colliders it contains<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. Whenever a rectangle is created or moved, I check if it moved out of a partition or into a new one and register or deregister it with that partition. Then, when doing collision checks with a rectangle, I first check which partitions that collider overlaps, and then only check for collisions with colliders registered to those partitions. If done properly, this cuts down massively on the number of collision checks that need to be done in one frame, great success!</p><p>So what about non-rectangle shapes? Different pairs of common shapes will have different &#8220;fast&#8221; ways of checking overlap, but rectangles are basically always fastest to check, so usually colliders will keep track of their shape as well as a &#8216;bounding box&#8217;, i.e. a rectangle that perfectly encloses the shape. Since most things aren&#8217;t overlapping most of the time, when checking for overlap between one or more non-rectangles, overlap between bounding boxes will be checked <em>first</em>, then the more expensive check will be done if and only if the bounding boxes overlap (since we can infer that the enclosed shapes aren&#8217;t overlapping if their bounds aren&#8217;t). This saves on doing a bunch of expensive checks. But what <em>are </em>those checks?? To be honest, for my purposes rectangles work 90% of the time, so I didn&#8217;t bother implementing any other common shapes. The other 10% is covered by &#8220;precise&#8221; collisions, i.e. irregular shapes, usually ones that conform to the exact shape of a sprite. To check these, I basically brute force it: for every &#8220;solid&#8221; pixel in shape A, I check if its position matches any solid pixel in shape B. This can be quite expensive, but it will happily handle any weird shape you can draw to a grid, and these sorts of collisions are so rare that it&#8217;s not usually a big deal. And if it ever does get to be a problem in a specific case, I can always just devise a more optimal solution for that specific case.</p><h1>Shaders</h1><p>Ugh, another topic that people can and have written whole textbooks about. Frankly, I&#8217;m not an expert on this, but basically the GPU&#8217;s shader is the program it runs which determines how it actually renders out texture data. Custom shaders can be used to produce all sorts of creative visual effects (as well as leverage the GPUs parallelism to do certain kinds of computations), but they&#8217;re always written in their own quirky languages due to said parallelism, and are limited in their ability to communicate with your main program.</p><p>Initially, these gave me a bit of trouble, since SDL2&#8217;s basic rendering backend, which handles making a lot of the more complicated calls to a graphics library such as OpenGL (which is what is ultimately making the CPU talk to the GPU), did not support shaders out of the box. Since I didn&#8217;t have the time to properly learn OpenGL and outright replace SDL&#8217;s functionality here I was in a bit of a bind. However, I figured out, through a lot of stressful research and trial-and-error, that by forcing SDL to use OpenGL as the rendering backend for its basic renderer you can make some simple calls to OpenGL to set a custom shader before calling SDL&#8217;s rendering functions, and the shader will work correctly. <em>Nowhere on the internet </em>is this explained explicitly, so, uh, you heard it here first folks!<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><p>Anyway, once all that was figured out, the rest was just a question of writing some shaders and adding them to the asset system. The sorts of shaders I want to use are few and fairly short, so I can get away with just packing the shader code (i.e. the text of the program, written OpenGL&#8217;s shader language, GLSL) in with the rest of my assets, then calling OpenGL when the game starts to compile that code into usable instructions that can be passed on to the GPU when needed.</p><p>Again, really not an expert here, I couldn&#8217;t yet tell you how half <a href="https://www.shadertoy.com/">the crazy stuff people do with shaders</a> is done, but getting started using shaders for applying simple effects to 2D textures is not that bad at all. </p><p>Each GLSL shader is split into two parts:</p><ul><li><p><strong>The vertex shader</strong>, which is only really relevant for 3D because it&#8217;s used to mess around with a 3D shape&#8217;s vertices, and so in my case will basically always look the same and not do much. If I wanted to deform the rectangle my texture was being drawn to this might be relevant, but usually I don&#8217;t.</p></li><li><p><strong>The fragment shader</strong>, which is used to mess around with pixel data by changing the position and value of colors on a texture. You can think the actual function written in it as being run on a single point of the texture (the GPU will run it on as many points as needed to construct each pixel of the final output). This is where the magic happens, by passing in a texture here, sampling it, modifying the sampled color, then passing it on, we can create all sorts of neat effects.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p></li></ul><p>And that pretty much covers it! If you want to get started with these yourself, I&#8217;d recommend looking up examples online, but here&#8217;s an example of a shader I wrote that creates a simple wavy effect and pulses the texture red:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P663!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P663!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 424w, https://substackcdn.com/image/fetch/$s_!P663!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 848w, https://substackcdn.com/image/fetch/$s_!P663!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 1272w, https://substackcdn.com/image/fetch/$s_!P663!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P663!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png" width="673" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:673,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25819,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!P663!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 424w, https://substackcdn.com/image/fetch/$s_!P663!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 848w, https://substackcdn.com/image/fetch/$s_!P663!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 1272w, https://substackcdn.com/image/fetch/$s_!P663!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587d7e0-bdef-4503-a915-b04cfd4934ca_673x276.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Vertex shader</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KtgT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KtgT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 424w, https://substackcdn.com/image/fetch/$s_!KtgT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 848w, https://substackcdn.com/image/fetch/$s_!KtgT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 1272w, https://substackcdn.com/image/fetch/$s_!KtgT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KtgT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png" width="668" height="371.3849938499385" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:452,&quot;width&quot;:813,&quot;resizeWidth&quot;:668,&quot;bytes&quot;:44361,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KtgT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 424w, https://substackcdn.com/image/fetch/$s_!KtgT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 848w, https://substackcdn.com/image/fetch/$s_!KtgT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 1272w, https://substackcdn.com/image/fetch/$s_!KtgT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed441f7c-ae9e-4873-8476-6d8a3568230b_813x452.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fragment shader. Setting gl_FragColor is what determines the output color. Doing this properly would necessitate passing in information about the sprite and texture page size in pixels, but since this is a quick test I&#8217;m just multiplying by some arbitrary constants.</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IwfA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IwfA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 424w, https://substackcdn.com/image/fetch/$s_!IwfA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 848w, https://substackcdn.com/image/fetch/$s_!IwfA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 1272w, https://substackcdn.com/image/fetch/$s_!IwfA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IwfA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png" width="678" height="349.8576512455516" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:435,&quot;width&quot;:843,&quot;resizeWidth&quot;:678,&quot;bytes&quot;:47466,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IwfA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 424w, https://substackcdn.com/image/fetch/$s_!IwfA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 848w, https://substackcdn.com/image/fetch/$s_!IwfA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 1272w, https://substackcdn.com/image/fetch/$s_!IwfA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfaf0e0-7f3b-44db-a7b3-4219ceb6c73b_843x435.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Example usage in main ODIN program</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZjkA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZjkA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 424w, https://substackcdn.com/image/fetch/$s_!ZjkA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 848w, https://substackcdn.com/image/fetch/$s_!ZjkA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 1272w, https://substackcdn.com/image/fetch/$s_!ZjkA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZjkA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif" width="132" height="134.1639344262295" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:124,&quot;width&quot;:122,&quot;resizeWidth&quot;:132,&quot;bytes&quot;:464414,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZjkA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 424w, https://substackcdn.com/image/fetch/$s_!ZjkA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 848w, https://substackcdn.com/image/fetch/$s_!ZjkA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 1272w, https://substackcdn.com/image/fetch/$s_!ZjkA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02391cb8-2ebb-4c3d-8eec-9e3e5a25d310_122x124.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Result, mangled by gif compression</figcaption></figure></div><h1>Serialization</h1><p>You can think of &#8216;serialization&#8217; as just a fancy synonym for &#8220;saving and loading (usually to a file)&#8221;, or, in a dry, useless, technical sense, &#8220;converting data into a stream of bytes for transmission or storage&#8221;. But it&#8217;s worth dissecting the term to understand what it really means: data in computer programs is, underneath it all, just a long string of ones and zeroes. But some of those ones and zeroes are treated as <em>references </em>to other bits of that long string. If I have 100 enemies in my game that all look the same, I don&#8217;t want to copy the texture data used to represent those enemies 100 times, what a waste! So instead, in each of the little data structures that hold each enemy&#8217;s information like position and speed, I keep a small little number that shows you exactly where to find that enemy&#8217;s texture data in some central place in memory. When the rendering system comes along to render that enemy, rather than reading the texture data to pass to the GPU directly from the enemy&#8217;s data itself, it follows that little address and gets it from the central place. This is all well and good, but now let&#8217;s say I want to save all of that enemy&#8217;s information to a file, so that I can load it up later. I tuck all the little numbers in the enemy&#8217;s little data structure into a little binary file and back it up to the cloud.</p><p>Flash forward 70 years. I am old. Too old. My only remaining shot at joy in life is to see that little enemy again. I pull the enemy&#8217;s file down from the hypercloud. Tell me dear reader, using only that file, will I be able to draw that enemy again&#8230;? No! Sure, I&#8217;ll know where that enemy was and how fast it was going, those are just numbers, stored safely in the file. But that little address that showed me where to get the texture data? It points to <em>nothing. </em>The RAM that held that texture data turned to ash long ago. Despair overtakes me, I will never see my enemy again.</p><p>This is what it really means for data to be &#8220;serializable&#8221; or not. If you can reconstruct all the relevant information a block of data is meant to represent by reading through that block alone, a.k.a. that <em>serial </em>string of ones and zeroes, then we can say that any data that can be turned into such a block is <em>serializable</em>. Conversely, data that isn&#8217;t serializable is data which points to outside information using addresses whose validity won&#8217;t outlive the data itself. Note that there&#8217;s a bit of fuzziness here. If I had loaded the enemy file right after saving it, without closing the program, the address pointing to the texture would still have worked (assuming the program didn&#8217;t move the texture data somewhere else for whatever reason). If I never intend to keep enemy files around for longer than the lifetime of the program, then I will always know how to interpret the texture address properly, and the enemy data is, in a limited sense, serializable.</p><p>Anyway, as you may have guessed, serialization comes into play when saving and loading the player&#8217;s progress, as well as when editing data that will get turned into loadable asset files such as stages or cutscenes. The exact details of how one goes about serializing this sort of data radically depends on the details of a specific game, but it&#8217;s important to think about how your data is laid out and to be able to quickly recognize what can and can&#8217;t be easily serialized.</p><h1>Stages</h1><p>A stage system is something that is genuinely so game-dependent that it&#8217;s tough to describe in detail. But <em>broadly speaking, </em>stage systems will look something like this:</p><ul><li><p>Stage data is saved to a file in a format your engine can interpret (i.e. deserialize).</p></li><li><p>These files are edited using and external editor such as Tiled or LDTk, or in-engine using a custom-built level editor. On my current project, I built an editor that can edit entities and a few other things like image and tileset layers directly. When saving, the editor reads only the relevent information from the game state and serializes it to a stage file. </p></li><li><p>When loading a stage, existing entities and other non-persistent stage elements are cleared, and new ones are created using information from the stage file. </p></li></ul><h1>Particles</h1><p>Gonna be honest, haven&#8217;t gotten around to actually building this yet as of writing this. But in principle, this is just a <em>very efficient </em>sprite system that sacrifices versatility and control over individual sprites in order to quickly be able to draw a huge amount of sprites at once. The difference is related to sorts of considerations that come up in Data-oriented design, something I touched on in the post on entities. Basically, the plan when building a particle system is to pack all the information your particles will need in order to be rendered (stuff like position, speed, spin, and texture information) as conservatively as possible into one dense array (it&#8217;s worth looking up other particle systems to get an idea of the kind of variables they use to create effects). You can then churn through this data very quickly, updating and rendering each particle without needing to retrieve data from elsewhere. </p><h1>Other tips and tricks</h1><ul><li><p>A debug console you can call up at anytime is an invaluable tool, and worth building. At its simplest this is just a textbox where you can type in predefined commands which will call custom functions, but adding features yourself such as a history, autocomplete, command arguments, and a visible output is not as daunting as you might think. I use the <a href="https://github.com/ocornut/imgui">dear imgui</a> library to build in-engine development tools like this, and it&#8217;s quite pleasant to work with.</p></li><li><p>Similarly, learning how to set up and use a profiler is not that hard and is indispensable when it comes to evaluating your code&#8217;s performance, and for a language manually managed memory like ODIN it&#8217;s immensely useful for making sure your program isn&#8217;t leaking memory (i.e. requesting memory without properly cleaning it up when its done, an error which will eventually balloon your program&#8217;s RAM consumption and crash it). I recommend <a href="https://github.com/wolfpld/tracy">Tracy</a>, ODIN bindings can be found <a href="https://github.com/oskarnp/odin-tracy">here</a>.</p></li><li><p>Tilesets should just be a small extension to the existing sprite system. Define a structure that &#8220;wraps&#8221; a sprite with a additional information such as tile size and padding. You can then build functionality that takes that structure as well as a given &#8220;tile index&#8221; and draws the relevant part of the sprite, i.e. the tile at the given index.</p></li><li><p>Regularly testing your engine on platforms you plan to support is a good way to catch bugs early and ensure you aren&#8217;t swamped tracking down errors in code you wrote months ago when it&#8217;s finally time to publish. Personally, I have not been keeping up with this, and am therefore screwed.</p></li><li><p>When working in ODIN and using a dynamically linked library, remember to include the library file with your final executable (.dll files on windows). Unlike a pure ODIN package, ODIN bindings just tell your program how to call a library, but if you don&#8217;t include the library itself alongside your .exe your program will fail when it tries to call the library and finds nothing. This does not apply to statically linked libraries (.lib on windows), which <em>will </em>be built into your .exe when compiling.</p></li><li><p>Slightly related to the above point, you can take advantage of .dlls to reload the majority of your engine&#8217;s code <em>while it&#8217;s running</em>, giving you the ability to change functionality without needing to close and reopen your program, which can help a lot with iterative testing. The exact details of this are too technical to cover in this series, but I&#8217;d recommend checking out <a href="https://github.com/karl-zylinski/odin-raylib-hot-reload-game-template">this repository</a> for details.</p></li></ul><h1>Grand Conclusion</h1><p>And there you have it! Thanks a lot for reading, to those who have or are planning to build their own engine I hope this helped make it all seem more approachable, looking back over those 6-ish months of work, it really does seem like modern languages like ODIN have made learning and doing proper low-level game development more accessible than ever before, even on a tight schedule like mine. And for those non-programmers this series is ostensibly aimed at, I hope this demystified a lot of what goes on under the hood of your favorite games and gave you a richer understanding of the medium.</p><p>And for those who are maybe on the fence about jumping into this kind of development, I really hope this gave you the push you needed to give it a shot. While I wouldn&#8217;t recommend it to a complete beginner, it&#8217;s clear to me now that building an engine like this isn&#8217;t the crazy multi-year ordeal some make it out to be, and that it should be a natural step on every game programmer&#8217;s journey. Especially for those developing 2D commercial projects, having to deal with a third-party engine can end up being far more trouble in the long run, and building your own foundation in a data-oriented way will result in a happier development experience and better software for your players. And best part is, you can do stuff like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p539!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p539!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 424w, https://substackcdn.com/image/fetch/$s_!p539!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 848w, https://substackcdn.com/image/fetch/$s_!p539!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 1272w, https://substackcdn.com/image/fetch/$s_!p539!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p539!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png" width="640" height="4800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:4800,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:919090,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p539!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 424w, https://substackcdn.com/image/fetch/$s_!p539!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 848w, https://substackcdn.com/image/fetch/$s_!p539!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 1272w, https://substackcdn.com/image/fetch/$s_!p539!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c8f3759-746d-40b9-b3af-32093b81aa5e_640x4800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Credit for original comic goes to <a href="https://www.instagram.com/crondle/?hl=en">crondle</a></figcaption></figure></div><h1>Onto the next thing</h1><p>Alright, I&#8217;m off to have my life entirely consumed by that project. See you soon!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fAlX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fAlX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 424w, https://substackcdn.com/image/fetch/$s_!fAlX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 848w, https://substackcdn.com/image/fetch/$s_!fAlX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 1272w, https://substackcdn.com/image/fetch/$s_!fAlX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fAlX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif" width="599" height="336" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:336,&quot;width&quot;:599,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:152734,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fAlX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 424w, https://substackcdn.com/image/fetch/$s_!fAlX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 848w, https://substackcdn.com/image/fetch/$s_!fAlX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 1272w, https://substackcdn.com/image/fetch/$s_!fAlX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7af2c5ef-6622-4fdb-8f99-a6f83f4c662a_599x336.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Stay tuned!</figcaption></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-b4d">&lt;Previous post</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>There are lots of complicated ways of divvying up a stage into partitions, but the simplest one is to just pick a size and split the stage up into big squares of that size. </p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>I believe this functionality has gotten much much better in the recently released SDL3, but at the time I was figuring this out I had no idea when it would be ready so was stuck with SDL2. Still, this is all kinda moot now, but if you want to know the details please comment below. </p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Annoyingly, since GPUs fundamentally work with stretchable triangles, they don&#8217;t really have an easy way to work with textures as fixed grids of pixels, so I often have to pass in information about a texture&#8217;s pixel size to get certain effects working properly. An easy example of this: say I want to make a shader that shifts everything in a texture to the right by a certain fixed pixel amount when drawing it. If I don&#8217;t know how big the texture is in pixels, I can only shift it to the right by, e.g. 30% of its size. This means larger textures would get shifted more in absolute pixel terms than smaller textures. </p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Audio]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-b4d</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-b4d</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Wed, 29 Jan 2025 06:43:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5f5eba9b-8993-4b9f-b7cf-161b8638d3f1_1440x860.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-008">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-ceb">Next Post&gt;</a></em></p><div><hr></div><p>just use FMOD lol</p><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-008">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-ceb">Next Post&gt;</a></em></p>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Input]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-008</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-008</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Wed, 29 Jan 2025 06:38:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/bd6becec-4325-4396-b1f1-88ea29c09e45_849x521.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-6fc">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-b4d">Next post&gt;</a></em></p><div><hr></div><p>Input. Seems simple, right? Press the A button, make the marketable character jump. Easy peasy. But what if I want to press the spacebar instead? What about the right trigger? What if I <a href="https://youtu.be/zQNpcX_1Syk?si=jcZrXN9NNUqlfnRj&amp;t=205">only want to use one hand</a>? What if I <em>wanted </em>to press the a button <em>now</em>, but I did it one tenth of a second ago? Everything&#8217;s falling apart. The intern has drizzled conditional input checks all over the player object. You&#8217;re using variables from 5 different files, half of which you don&#8217;t remember making. You&#8217;re tracking input buffers in the player&#8217;s state logic. Nightmare. Nightmare! NIGHTMARE!!</p><p>Like many software development tasks, building a proper input system is an exercise in abstraction. The goal is to translate signals from a controller into actions in the game, but problems arise when thinking of this as a 1:1 relationship. Instead, it&#8217;s often simpler in the long run to construct multiple <em>abstraction layers </em>which store information and mappings and serve to translate concise, high level requests (e.g. did the player make a &#8220;jump&#8221; input?) into questions of the lower level hardware (e.g. did the 3rd face button on the gamepad connected to OS port 2 go from unpressed to pressed within the last 10 frames?).</p><p>Now, the exact construction of these layers is going to vary significantly based on your preferences, needs, and what your framework/engine can already provide for you, but I&#8217;ll describe the system I use, which is fairly generalizable. It involves roughly 4 layers:</p><ul><li><p>The framework layer</p></li><li><p>The abstracted hardware layer</p></li><li><p>The mapping layer</p></li><li><p>The verb layer</p></li></ul><h2>The framework layer</h2><p>Premade engines like Game Maker or Godot will usually get you most of the second layer out of the box, sometimes more, but if you&#8217;re just using a framework like SDL then this is the only part that will already be done for you. In my case, SDL handles two things for me:</p><ul><li><p>Allows me to retrieve the complete input state of the keyboard or mouse at any given point in time as a big chunk of data.</p></li><li><p>Triggers events when a gamepad is connected/disconnected, which provide a pointer that can be used to retrieve infromation about the state of that specific gamepad later on.</p></li></ul><h2>The abstracted hardware layer</h2><p>The goal of this layer is to read the input states obtained from the previous layer and allow us to easily ask, for any given device, whether one of its keys/buttons<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> was pressed, released, or is being held. Also to retrieve the current value of unique stuff like analog stick axes or mouse position. I call it the &#8220;abstracted hardware&#8221; layer because, while we&#8217;re still thinking of physical devices at this point, I&#8217;ll be fully abstracting away anything that isn&#8217;t directly relevant to input; stuff like what exact usb or hardware slot we&#8217;re using, or weirdly formatted analog input values. </p><p>For the most part, we can just pass on slightly reformatted values from the framework layer, but there <em>are </em>two not-so-trivial problems to solve here:</p><ul><li><p>How do you tell if a button has just been pressed/released, given that we can only retrieve the current input state at any given time?</p></li><li><p>How do you handle multiple gamepads?</p></li></ul><p>Taking them in order, the first problem is simple. As mentioned previously, the game runs it&#8217;s logic once per &#8220;frame&#8221;, in my case every 1/60th of a second. While some applications will try to obtain the input state as often as they can, in my case I only have to do so at the start of each frame. But I can also store the input state from the start of the previous frame as well! By doing this, whenever something asks if a key/button was pressed, I just need to check the current state against the previous state. If the button was inactive last frame but is active this frame, that means it was just pressed down<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>!</p><p>The next problem is a little trickier. If you&#8217;re making a singleplayer game you can get away with just polling every connected gamepad for input and treating them like one weird schrodinger&#8217;s gamepad, but if you&#8217;re doing any sort of multiplayer you need to be able to differentiate between each specific gamepad; you need to assign each a unique, persistent ID. </p><p>I see many beginners inclined to just look up a controller&#8217;s &#8216;hardware slot&#8217; (the number sometimes indicated by little green lights on controllers) and use that to identify it. Unfortunately, this runs into a number of pitfalls, mainly because it leaves you completely beholden to what the OS wants to do with this ID. Did you know that on Windows it&#8217;s <em>almost impossible </em>to change what ID a controller has been assigned without restarting your PC? If you plug in two controllers then unplug the first one, well, congrats: barring a restart (or a 15 minute unplug-replug sesh with 3 more controllers) you are Player 2 forever now. The number of games that don&#8217;t account for this is staggering, I&#8217;ve seen some <em>singleplayer </em>games that refuse to read anything from any gamepad except the one in the first slot. So this is a non-starter. </p><p>My recommended alternative is to assign virtual slot numbers yourself, in order, whenever a gamepad is plugged in. If a gamepad is unplugged it&#8217;s slot becomes available to the next gamepad that&#8217;s plugged in, so resetting slots is as simple for the user as unplugging everything then replugging it in the desired order. This does mean stuff can get mixed up if something is accidentally unplugged after someone leaves, but I think the gains in flexibility more than make up for this shortcoming.</p><p>And that&#8217;s that! A layer like this is useful in and of itself when doing stuff like prototyping or simple menus, but if things need to be remapped then we need to move on&#8230;</p><h2>The mapping layer</h2><p>At this point, things would start to get a little more game-dependent. This part of the input system wouldn&#8217;t usually be done as part of the base engine, but the idea can be applied to almost any game so I figured I&#8217;d cover it anyway. I&#8217;ll be assuming the game is singleplayer to keep things simple, but the idea would be the same in multiplayer games, just copied across multiple profiles.</p><p>This layer handles mapping (or &#8216;binding&#8217;) buttons to in-game actions, i.e. defining what will be shown to the player in the in-game input remapping menu. You wouldn&#8217;t typically want to allow this for stuff like simple menus (since the player could inadvertently softlock their program) but, in my opinion, any other in-game action should be on the table here. The exact actions and default mappings will, of course, depend on the game.</p><p>There&#8217;s not much else to say here, this layer basically just exists as a big switchboard. You ask it &#8220;what&#8217;s the button for jumping?&#8221; and it knows it&#8217;s the A button, which it can then pass along to the hardware layer to know whether that button is being pressed.</p><h2>The verb layer</h2><p>Finally, we&#8217;ve reached the end point. Here&#8217;s where things get a little interesting. You might have a &#8220;jump&#8221; button mapped in the previous layer, but sometimes just knowing if the &#8220;jump&#8221; button was pressed this frame isn&#8217;t enough. The main purpose of this layer is to be hardware agnostic (i.e. to check for an action&#8217;s corresponding input regardless of what input device the player is using), but it&#8217;s also used to implement <em>input buffering, </em>i.e. &#8220;storing&#8221; inputs for use in the near future. A common example that&#8217;s often given as a use-case for this goes like so: in platformers, players often want to jump as soon as they hit the ground, but if they press the jump button slightly before landing then the input will be &#8216;eaten&#8217; (since you can&#8217;t jump in midair), which feels terrible. But if you &#8216;buffer&#8217; the input by checking (when grounded) not only if the jump button was pressed this frame but <em>at any point in the last 10 frames, </em>then the player character will jump as soon as they hit the ground even if the input came slightly too early. The exact amount of buffer will vary from action to action and game to game (non-action games usually have no need for it at all), but granting this sort of margin of error goes a long way in making a game feel responsive.</p><p>So the end goal of this layer is to provide a list of &#8220;verbs&#8221;<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> that can be used to make requests like &#8220;was there a [jump] input in the last 10 frames?&#8221;. You might have noticed that I already do a little bit of buffering in the abstracted hardware layer in order to check button presses and releases, and while I could handle more long-term buffering at that stage, doing it here (by storing a portion of the data from that layer) is a bit more efficient since I only have to keep track of the inputs the player has mapped to an action, rather than the hundreds of possible buttons that <em>could </em>be mapped. </p><p>Each action from the mapping layer usually gets 1-3 verbs (depending if you want to track a button press, release, hold, or some combination of the three). You&#8217;d also have verbs for actions that don&#8217;t necessarily have a mapping, such as left and right movement when using a gamepad (since that&#8217;s usually locked to an analog stick). Again, the point here is to be completely hardware agnostic, the layer will handle identifying what kind of controller the player is using and making the right request to the hardware layer using the correct bindings from mapping layer (or constant values, for actions with no bindings). It stores information for all these verbs every frame in its buffer, and uses this information to answer the aforementioned high-level requests.</p><h2>Conclusion</h2><p>Awesome! And with that, we&#8217;ve covered every &#8220;fundamental&#8221; feature a game engine needs to make an absolutely terrible super bare-bones &#8220;&#8220;&#8220;game&#8221;&#8221;&#8221;! Wow!<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a> From now on I&#8217;ll be talking about some important but less totally foundational engine features; join me next time to <em>hear </em>all about the audio system! </p><p></p><p>And by hear I mean read, I will not be including audio.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-6fc">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-b4d">Next Post&gt;</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Referred to henceforth just as &#8220;buttons&#8221;.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>And vice-versa to check if a button was released.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Calling them &#8220;actions&#8221; felt too ambiguous</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Well, we probably didn&#8217;t really <em>need </em>sprites, you could make a game with just, like, rectangles.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Sprites]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-6fc</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-6fc</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Thu, 25 Jul 2024 23:45:51 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/710e491f-1ba1-4d4b-bd51-9fcf2707f034_458x348.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-86a">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-008">Next Post&gt;</a></em></p><div><hr></div><p>Y&#8217;know, I&#8217;ve always wondered why they&#8217;re called that. Wikipedia claims it&#8217;s because they &#8220;float on top of the background image without overwriting it, much like a ghost or mythological sprite&#8221;, but that seems awfully tenuous. </p><p>Anyway, what exactly are we talking about? Well, images! This is fairly common knowledge, but &#8216;sprite&#8217; is the general term for any pre-drawn 2D image you might see in a game. They&#8217;re used to depict anything from characters to scenery to UI elements. One important thing to note: self-contained animations (such as a character&#8217;s walk cycle) might contain several individual images (i.e. animation frames), but they&#8217;re usually considered a <em>single </em>sprite. This makes sense, for reasons we&#8217;ll see later.</p><p>Now, what exactly goes into a sprite system? The goal when building one can be summarized thusly: </p><ol><li><p>Get the drawing from the artist&#8217;s desk.</p></li><li><p>Convert it into something that can be rendered to the screen efficiently by the computer.</p></li><li><p>Make a system the programmer can easily use to tell the computer to do so whenever they want.</p></li></ol><h2>Get the drawing!</h2><p>Our precious, precious artists. They are beautiful, innocent, unsullied. They are not to be exposed to anything as filthy as a command line. As such, I must find a way to automatically retrieve their work. Fortunately, we can make this step a part of our build system (see the previous post for details about that). </p><p>As mentioned last time, artists will be mostly working with Aseprite files. This is great for them, since they have all sorts of editing tools at their disposal and can work on an entire animation at once, but it presents an issue, since most tools can&#8217;t read aseprite files directly. Not to worry though! Thanks to Aseprite&#8217;s scripting API I have the ability to automate the export of these files to some pretty precise specifications. Since I need the individual image data of each animation frame for the next step anyway, I&#8217;ll export each frame stored in the aseprite file as an individual .png<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> file and store it in a temporary folder.</p><p>One additional wrinkle: I need the image data, sure, but I also need to keep track of some metadata for each frame; stuff like the name of the sprite, the order of the frame, how long it lasts in the full animation, and a custom origin point. .png files don&#8217;t exactly have a predetermined slot for this data, so it&#8217;s not obvious where it would go. One option is to give the file a unique ID then create a separate file when exporting that tracks this data and maps it to the file id (which I&#8217;ll have to do later anyway), but, given that it&#8217;s not actually a lot of data at this point, I can use a neat trick and simplify things by storing that information in the file&#8217;s title! Here&#8217;s an example of an exported 4-frame animation; the folder name keeps track of the name of the sprite and each file title stores the frame index, duration (in milliseconds), and origin point:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c8gO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c8gO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 424w, https://substackcdn.com/image/fetch/$s_!c8gO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 848w, https://substackcdn.com/image/fetch/$s_!c8gO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 1272w, https://substackcdn.com/image/fetch/$s_!c8gO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c8gO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png" width="1180" height="624" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1180,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69839,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c8gO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 424w, https://substackcdn.com/image/fetch/$s_!c8gO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 848w, https://substackcdn.com/image/fetch/$s_!c8gO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 1272w, https://substackcdn.com/image/fetch/$s_!c8gO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe99d1734-bb63-4266-b75c-5cd5d19f6c4d_1180x624.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Convert the drawing!</h2><p>Alright, now that I have the images, I need to be able to render them efficiently in-game! This involves retrieving the image data from the files when the game is running, storing it in a &#8216;texture&#8217; (essentially a blob of image data that lives in RAM), then rendering that texture to the screen using the GPU. The framework I&#8217;m using can do this by loading .png files directly into their own individual textures and using those, but this should be avoided for two reasons:</p><ol><li><p>A game can end up having thousands or even tens of thousands of individual animation frames. Loading them all into memory separately while the game is running would involve just as many filesystem calls, which as I&#8217;ve mentioned before can be quite bad for performance.</p></li><li><p>This one&#8217;s more important, and the reason we can&#8217;t use a simple asset packer to get around the first problem: rendering a texture involves the CPU moving it from RAM to the GPU and&#8230; hm, I feel I should maybe explain these terms&#8230;</p></li></ol><h2>Sidebar!</h2><p>Well dear reader, you&#8217;ve done great so far, but it&#8217;s unofrtunately time to subject you to another computer science lesson (tragic). If you know what a GPU is you can skip ahead to the next heading, but for everyone else, sorry, but I&#8217;ve already locked the doors. Now then, in <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-901">the second post in this series</a> we talked a bit about the CPU and RAM. As a quick refresher:</p><ul><li><p>The CPU (Central Processing Unit) is the part of your computer that does most of the &#8220;work&#8221; by moving bits of data to and from memory and running simple opertions on them.</p></li><li><p>RAM (Random Access Memory) is the type of memory in your computer that&#8217;s only used while the computer is on. It&#8217;s faster to access than permanent storage like your hard drive, and most or all of a program&#8217;s information will be stored here while that program is running.</p></li></ul><p>Understanding these is enough to get an idea of what most programs are doing, but once we add certain tasks (like graphics) into the mix, we have to start talking about the GPU! If you&#8217;re unaware, that stands for &#8216;Graphics Processing Unit&#8217; (big surprise). It&#8217;s a processing unit just like the CPU in the sense that it receives data from memory, does stuff to that data, then sends it on it&#8217;s way. In fact, older PCs didn&#8217;t even have any, they weren&#8217;t even invented until the late 90s! So what exactly is the difference? </p><p>It all comes down to something called <em>parallelism</em>. Take the following computing task:</p><ol><li><p>Read number a and number b from memory.</p></li><li><p>Add them together.</p></li><li><p>Take the result and divide it by 2.</p></li><li><p>Save the result to memory as number c.</p></li></ol><p>This task can be described as <em>sequential</em>, that is, each step depends on the result of the previous step. More accurately, you would say it&#8217;s impossible to do better than the sequential approach when trying to complete this task. Now let&#8217;s take a look at a different example:</p><ol><li><p>Read number a, number b, and number c from memory.</p></li><li><p>Add 4 to number a.</p></li><li><p>Subtract 2 from number b.</p></li><li><p>Multiply number c by 0.</p></li><li><p>Save the results to memory in-place.</p></li></ol><p>One thing you might have noticed here: none of the operations on the numbers here really depend on what&#8217;s going on with the other numbers, which means this problem is <em>parallelizable, </em>i.e. the steps can be worked on in parallel. </p><p>To use a simplified analogy, let&#8217;s say your CPU contains 3 little worker elves. If you gave the elves the first task, it doesn&#8217;t really help that there are three of them. Even if each elf is assigned it&#8217;s own steps, the elf assigned to step 2 would still have to wait around for step 1 to finish, and the elf assigned to step 3 would have to wait for steps 1 and 2 to finish. This means it would take the same amount of time for one elf to do all the work as it would take for 3, 10, or even 100 elves! In fact, more elves might take longer, since they need to pass things around to each other. But when it comes to the second task, more elves helps a lot! Each elf can work on its own number without caring about what the other elves are doing, and things get done 3 times faster than if one elf had to go through all three numbers itself!</p><p>So what does this have to do with anything? Well, you can think of the main difference between the CPU and GPU as being their ability to work on things in parallel. For a while, CPUs could <em>only </em>work on tasks sequentially, and this is still their main strength today. The CPU in your typical home computer has about 2-8 elves (or, er, &#8216;cores&#8217;). Now, these are really jacked elves, they eat their protein powder and drink their juice; they can churn through tasks <em>really </em>quickly. But there are only 8 of them. Conversely, GPUs can have <em>thousands </em>of cores. Now, most computing tasks are sequential, the only way you&#8217;re getting things done faster is by getting a faster single core. But certain classes of tasks (such as, surprise surprise, graphics) are referred to as &#8216;embarassingly parallel&#8217;, which means more cores &#8594; faster performance. </p><p>To make this a little more salient, let&#8217;s take a typical graphics task: drawing a tinted HD image to the screen. There are 2,073,600 pixels in an image that size (1920x1080). If I were doing things sequentially, I&#8217;d have to operate on the color data of each individual pixel one at a time; <em>millions </em>of operations. But none of the pixels care what the other pixels look like. With a GPU, each core can work in parallel to work on thousands of pixels at a time, drastically speeding up the operation. Waow!</p><h2>Where were we?</h2><p>Huh? Oh yeah, sprite packing. So, why can&#8217;t all my sprite frames stay in their own little .png file? This is because, in order to get the GPU to do anything, the CPU has to send the data relevant to the task to it from RAM, which can often be the slowest part of the process. If each sprite is stored as its own texture in memory, then I&#8217;d have to send over a new texture every single time I want to draw a sprite, very slow. </p><p>So how does one get around this? Well, textures can get very big; many modern GPUs support textures up to 4096x4096 pixels in size, and almost all support textures up to 2048x2048 pixels. Since most sprites are much smaller than this (especially ones in a retro-style pixel-art game), this means I can &#8220;pack&#8221; more than one sprite on a single texture! When drawing a sprite frame, I send this whole giant texture, then tell the GPU to only draw the specific part corresponding to that specific frame. This might seem wasteful, and it would be if I was doing this every time I wanted to draw the sprite frame, but organizing things this way allows me to &#8220;batch&#8221; draw calls. Instead of sending the texture over every time I want to draw a sprite, I instead keep the drawing instructions in reserve. When it&#8217;s time to present a finished render to the screen, I send the whole texture over <em>along with all the saved up instructions at once. </em>This means the GPU will happily draw a bunch of sprites all using the same texture!</p><p>So how do I get these &#8216;packed&#8217; textures? It&#8217;s technically possible to load a bunch of separate .pngs into one texture when the game starts up, but, as mentioned, it&#8217;s better to do this step ahead of time. This means packing all my smaller .pngs into one big .png known as a &#8216;texture page&#8217;<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> (a.k.a. texture atlas, sprite atlas, or sprite sheet<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>). At this stage, since sprites are no longer split up into a bunch of different files, I also produce a separate &#8216;index&#8217; file for each texture page that keeps track of sprite frame metadata (the stuff we saw earlier, like frame index and duration, as well as where that frame is positioned on the texture page). I can then read the data from this file when drawing to calculate the exact instructions I need to send to the GPU.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1Xrx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Xrx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 424w, https://substackcdn.com/image/fetch/$s_!1Xrx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 848w, https://substackcdn.com/image/fetch/$s_!1Xrx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 1272w, https://substackcdn.com/image/fetch/$s_!1Xrx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Xrx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png" width="1437" height="301" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:301,&quot;width&quot;:1437,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39486,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1Xrx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 424w, https://substackcdn.com/image/fetch/$s_!1Xrx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 848w, https://substackcdn.com/image/fetch/$s_!1Xrx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 1272w, https://substackcdn.com/image/fetch/$s_!1Xrx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa86fae67-f3a2-41d5-8473-af7d3715a745_1437x301.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Example of a (small) finished texture page.</figcaption></figure></div><h2>Make a system the programmer can easily use to tell the computer to draw sprites whenever they want!</h2><p>Ok! Ok! We sort of already covered most of this in the previous section, but here are some neat things I included in my sprite system to make using it easier:</p><ol><li><p>Part of the build process saves each sprite&#8217;s name to a code file in a special way that lets me refer to each sprite directly by name when scripting.</p></li><li><p>The most basic way to draw a sprite is by providing the sprite, as well as a position. I built my system to be able to do this by without the requester needing to deal with any of the texture page stuff.</p></li><li><p>Normally, when drawing a sprite at a given position, it will draw the top-left corner of the sprite there. To make things easier on me and the artists, I added the ability to specify a special &#8220;origin&#8221; layer in Aseprite, which is tracked by the exporting script. I mentioned this briefly earlier, but basically the &#8216;origin&#8217; is the point on the sprite that will drawn at the given position. Being able to change this makes it easier to reason about where to draw a sprite (e.g. we usually want every entity&#8217;s position in a platformer to be set at their feet. If their sprites were of different heights, we&#8217;d have to determine where the top of the sprite would go for each entity in order to draw it. But by setting every entity&#8217;s sprite&#8217;s origin at that sprite&#8217;s feet, we can simply draw the sprite at the entity&#8217;s position with no adjustment necessary!). The origin can also be used as the rotation axis when drawing the sprite at an angle.</p></li><li><p>Scaling the sprite means mapping it to a differently sized rectangular area, but it&#8217;s often easier to think about multiplying a sprite&#8217;s scale. I wrote some code that lets me use that method instead, it also scales relative to the origin, so e.g. an enemy that gets scaled up will still have its feet on the floor.</p></li></ol><h2>Nice!</h2><p>Yup.</p><p>Next time I&#8217;ll talk about the input system, stay tuned!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-86a">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-008">Next Post&gt;</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>We use .png files since they&#8217;re a lossless format that simply stores the image as-is. While it&#8217;s technically possible to use a compressed format like .jpg, this would be counterproductive: compression would damage the image quality of sprites, and rendering the sprite would require either decompressing it ahead of time (defeating the point of compression), or slowly decompressing it whenever it needed to be rendered, which would be terrible for performance. The only time this <em>might </em>make sense is for <em>extremely </em>large image files.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>There are several existing software tools that can do this, google &#8220;texture packing&#8221; and pick your favorite!</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Not to be confused with the &#8216;sprite sheets&#8217; you might have seen on websites like The Spriter&#8217;s Resource. These are usually reconstructed manually by extracting image data from game files and lining it up in an appealing way. They are meant to serve as a clean reference for artists rather than an optimized chunk of data.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Building the Game]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-86a</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-86a</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Fri, 05 Jul 2024 06:06:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5a754d45-d104-4ac1-87ec-1ded0c4f73af_1198x797.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-bd8">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-6fc">Next post&gt;</a></em></p><div><hr></div><p><em>I released a demo video recently showing off a bunch of the engine&#8217;s basic features! Here it is in case you missed it:</em></p><div id="youtube2-jUHQJ-m6xOI" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;jUHQJ-m6xOI&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/jUHQJ-m6xOI?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><em>I&#8230; might be a bit further ahead than these posts maybe had you believing. Can&#8217;t spend all my time on my blog after all! So, uh, sorry for spoiling the next few posts I guess.</em></p><div><hr></div><p>Now that the engine code has a functional core in the entity system and game loop, I can start actually writing code that will, y&#8217;know, do stuff.</p><p>Like the test app I wrote, setting up a display system involves just a few calls to the framework<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. I&#8217;ll talk about what goes into a fully-fledged display system more in a later post, but for now I&#8217;m just setting up a simple texture I can draw to and clear every frame (sort of like a digital canvas), then scaling that texture to an application window displayed to the user. Once that&#8217;s done, I can write code in the main loop or in entity components that draws stuff to that texture, and it&#8217;ll show up on screen!</p><p>At this point I&#8217;m still kind of limited though. The framework provides functionality for drawing &#8216;primitives&#8217; like lines and triangles, but last I checked it&#8217;s not 1962 anymore, it&#8217;s <em>at least </em>1974<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>; time to get some sprites in! To do that, I&#8217;ll need to load some kind of image data into memory. I could just use the framework to load individual .png files directly, but once a game starts using a lot of sprites this can get quite slow for several reasons, so I&#8217;ll want to pack sprites into larger image files. Also, hm, how am I gonna organize all those files? These are <em>animated </em>sprites, each frame is its own image file! And anyone doing art is probably gonna be using a program like aseprite, are they gonna have to export and sort everything themselves? And where am I gonna put other assets like text files or sounds? Come to think of it, compiling the engine code manually every time from my command line is getting kinda tedious&#8230; </p><p>Ok, it&#8217;s about time to set up a <strong>build system</strong>.</p><h2>What&#8217;s that?</h2><p>Build systems come in many shapes and forms, but they can generally be defined as &#8220;automated processes that turn a program&#8217;s source code and other asset files into a set of files that can be run directly&#8221; (for simple programs, you could consider the &#8220;build system&#8221; as just being the compiler which turns your source code into a single .exe file that anyone can run, but, as we just saw, more complex programs like games tend to have more elaborate requirements). If you pop open your steam directory and open up any game&#8217;s install folder, you won&#8217;t (often) be able to see a game&#8217;s source code or view/edit its assets directly, what you&#8217;re seeing instead is the output of that game&#8217;s build system.</p><p>So what are my goals? Actually, pretty specific. The shortcomings of Game Maker&#8217;s build system remain a thorn in my side to this day, creating something that avoids making the same mistakes is a huge reason I&#8217;m developing my own engine in the first place. With that in mind, here&#8217;s the plan:</p><ul><li><p>Any part of the game&#8217;s files that can be built individually should be. No need to rebuild every sprite when changing a line of code.</p></li><li><p>An automated file watching system to ensure that files are rebuilt as soon as a change is detected. This means the build system is always running in the background to ensure the game is ready to be launched as soon as possible. Normally, a build system would only start running the moment the user manually triggered it, forcing them to wait for it to finish before the program actually launched. This helps avoid that<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>.</p></li><li><p>Asset packing, which is basically just squeezing the raw data from multiple asset files into one big file that&#8217;s just a blob of ones and zeroes. This speeds up loading times in-game by reducing the amount of file-system requests I need to make, as well as slightly obfuscating the assets to make it harder for inexperienced users digging into the game files to mess with them.</p></li><li><p>Support for building .aseprite files. Aseprite is basically the industry standard for pixel art, and is what&#8217;s typically used to make the bulk of the art and animations for the sorts of games I&#8217;m developing. Unfortunately, like most image editing programs, it&#8217;s not standard <em>enough </em>that its project files can be directly read in the same way that .png files can. This usually means that aseprite project files need to be exported to a set of intermediary files in order to actually be legible. But automating this process as part of my build system (as well as exporting additional data such as frame timings) would allow me and other team members to work directly on aseprite files and see the results reflected in-game without any need to manually export things, a huge quality-of-life improvement.</p></li><li><p>A quick way to both run the current build and to initiate a new build from scratch. Having e.g. a keyboard shortcut to run the game makes it nice and easy to test changes on a whim. And initiating a build from scratch might take longer than running using any existing built files, but it&#8217;s a crucial ability to have given the automated file watching process might introduce errors or fail to keep up in rare cases.</p></li><li><p>Ability to &#8216;hot-reload&#8217; assets at runtime. What this means is that it should be possible to change a non-code asset after the game has been launched and see the changes show up in-game. Now, there&#8217;s a limit to how robust this can be without putting in a ton of effort, but even a simple, slightly unstable version of this is incredibly useful as a developer-only tool.</p></li><li><p>Speed! The build system should be as fast as possible, wherever possible. This is a huge deal when it comes to iterating on a game, you want to be able to see changes you make reflected in-game as soon as possible. If it takes too long, your attention will wander and maintaining a tight iterative feedback loop in your own head becomes much harder. Say you wanted to tune a character&#8217;s walking and running speed to be <em>just </em>right. How much easier would that be if it took half a second for your changes to show up vs. 7 minutes? You might have noticed a lot of the features listed here are designed to cut down on the time a developer spends idly waiting; that&#8217;s all in service of the same goal.</p></li></ul><p>Lofty goals! So how do I go about making something like this? It can&#8217;t quite be a part of the rest of the source code, that&#8217;s what we&#8217;re trying to build after all! In theory, I could also make all this using odin, it would just be a smaller, simpler set of programs that would be stored and compiled separately. This might even be one of the more performant options. But to be honest, it&#8217;s not exactly what the language was built for. A systems language makes sense when you&#8217;re trying to quickly manipulate a large, complex set of data in RAM. But most of the operations involved in a build system will either be requests to the filesystem or to other programs, which means the program instructions themselves won&#8217;t make up that large a portion of the overall execution time. </p><p>In that case, it makes more sense to look at a some sort of scripting language designed for these sorts of operations. The gain in ergonomics from e.g. not having to constantly recompile small programs (since interpreted scripts can just be run directly without needing to first compile them) or deal with odin&#8217;s more finicky text string manipulation seems like it would greatly outweigh the possible performance downsides. A popular language like python or lua could do the trick, but ultimately I decided to write most of my scripts in powershell, microsoft&#8217;s scripting language designed specifically for these sorts of tasks. And yeah, even at first glance it&#8217;s obvious that powershell makes it very easy to do file operations and to execute command line programs (it&#8217;s also probably as optimized as it gets for that sort of stuff&#8230; for an interpreted language). There&#8217;s definitely some syntax weirdness in other places, but it&#8217;s clearly the right tool for the job.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a></p><p>Still though, the self-contained nature of each script makes it much easier to incorporate other tools into the build system. Jumping ahead a little, I ended up using a small lua script as part of the sprite exporting process to take advantage of aseprite&#8217;s scripting API, and the aformentioned asset packer ended up being written as a small odin program, since it involves manipulating a lot of raw data.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a></p><h2>And file watching?</h2><p>So, tracking changes to files is a problem without an obvious approach. A while back, my thought was that I&#8217;d have to keep some kind of ledger that could be quickly checked against every time a build occured to see if anything had changed. Fortunately, after looking into it some more, I found out that there are a number of fleshed out <strong>file watching </strong>programs that can be customized to suit one&#8217;s needs. A file watcher is a program that runs in the background and periodically checks a specified directory for changes, and which can do stuff in response. I&#8217;m sure the details of how it does this efficiently are fascinating, but fortunately I won&#8217;t have to bother with them!</p><p>I decided to go with Watchman, a program developed at Facebook. It seems to be intended more for web stuff, but it&#8217;s simple and versatile enough to work well for me. By setting up a series of &#8220;triggers&#8221; (again, via a powershell script), I can have watchman pass information about individual changed files to my other scripts, to do with what they will.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a></p><h2>Waow!</h2><p>That&#8217;s pretty much how the build system works, at a high level. To summarize, Watchman is initialized the first time the user builds the game, watches for changes in files in a project&#8217;s asset folder, and any changed files get processed by scripts and outputted into a dedicated &#8216;build&#8217; folder. The build in this folder can then be run using a special script, tied to a keyboard shortcut, that makes sure everything&#8217;s done building correctly before opening the game&#8217;s executable.</p><p>For code, rebuilding is about as simple as calling the odin compiler whenever a change is detected in a .odin file<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a>, but other types of assets each have their own special little process. Since each process closely tied to how that type of asset is used in-engine, I&#8217;ll talk more about them in detail when I get around to how that asset type is handled. And in fact, next time I&#8217;ll get back to the sprites, so look forward to it! But for now, you can check out the build system in action in the demo video linked above!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-bd8">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-6fc">Next post&gt;</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>See <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-901">this post</a> for more information about game dev frameworks.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>I really hope wikipedia got these dates right.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>The more astute among you might realize that this tactic won&#8217;t help that much in cases where the user makes a change then wants to run the game <em>immediately</em>,<em> </em>but it&#8217;s often the case that some change will be made long before the user wants to actually run the game (such as when pulling a change from a remote repository or making several edits in a row to different assets). In cases like these, it prevents rebuilding work from piling up. It also eliminates the need to manually track and initiate rebuilds of specific individual assets.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Also, since powershell is part of the Windows OS, scripts can be run very easily, and it&#8217;s one less thing that needs to be installed for anyone else trying to use the engine. This does unfortunately lock me even more into working on Windows, but, frankly, being able to test on the actual OS that most end users will be on is the <em>main</em> reason I&#8217;m stuck here. If you don&#8217;t have this problem, do your part and switch to linux today!</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>How exactly am I packing my assets? There are a lot of solutions out there for asset packing file formats, but the problem seemed easy enough that I ended up deciding to just write my own dead simple format. This gets a bit technical, but essentially I&#8217;m just writing a stream of raw bytes to a file. For each type of asset, the program will search a provided directory for the requisite files. It then writes the type of asset (each type is a assigned a numerical ID) and the number of assets of that type. Then, for each file, it writes the size of the file in bytes along with the raw data from that file. Then, once the game is running, I can load all the packed assets from the file just by running the same process in reverse.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p>One convenient Watchman feature: when restarting the program, it will treat all existing watched files as newly changed, which means it will pass them all along to their corresponding scripts at once. This makes it very convenient to build the game again from scratch!</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p><em>About </em>as simple. I ended up deciding to autogenerate some code for components due to the limitations of Odin&#8217;s strict type system, as well as wanting to add a pinch of syntactic sugar of my own in component event code using special comments. Fortunately this turned out to be not that bad.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: The Game Loop]]></title><description><![CDATA[How do framerates actually work?]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-bd8</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-bd8</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Tue, 18 Jun 2024 03:25:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7a74bb5e-661d-463a-a72f-3583739c6cc6_419x263.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-c31">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-86a">Next post&gt;</a></em></p><div><hr></div><p><em>(Note: not to be confused with the game design term)</em></p><p><a href="https://gameprogrammingpatterns.com/game-loop.html">Game Programming Patterns has a pretty good chapter on these</a>:</p><blockquote><p>If there is one pattern this book couldn&#8217;t live without, this is it. Game loops are the quintessential example of a &#8220;game programming pattern&#8221;. Almost every game has one, no two are exactly alike, and relatively few programs outside of games use them.</p></blockquote><p>If you have some intermediate coding knowledge you might be better served by that article, but I&#8217;ll try to summarize for the rest of you.</p><p>If you think of a whole program as a big tree of nested function calls (i.e. a bunch of code which executes more code which executes more code), then this is the root. A game (actually, almost any modern application) doesn&#8217;t end until the player asks it to stop. This means it needs to keep running in an endless loop until that happens, continuously waiting for input and reacting to it when that happens. Most games also differ from modern applications in that things keep happening even when the player isn&#8217;t touching anything. Enemies keep moving, animations keep animating, physics keep being simulated, etc. This means that, once the game is initialized, you naturally end up with a looping set of instructions like this:</p><ol><li><p>Read the player&#8217;s input</p></li><li><p>Update the game (physics, player character, enemies, logic, animations, etc.).</p></li><li><p>Draw the updated game state to the screen.</p></li><li><p>If the player didn&#8217;t quit, go back to step 1. Otherwise, close the game. </p></li></ol><p>Easy-peasy, right? Well, not quite. If you set up some code to run in an endless loop, it will just run as fast as the CPU will allow it to. This means that, depending on several factors such as the hardware or how intensive the game&#8217;s current workload is, the game will run faster or slower, resulting in constant fluctuations in the simulation speed. For example, say I set an enemy to move forward by 0.1 units on every loop. The loop might run 500 times per second on my PC, but only 100 times every second on B-san&#8217;s PC. This means the enemy would be moving 50 units per second for me, but only 10 units per second for B-san. <em>Well </em>too slow. Poor B-san. So, how do we fix this? There are multiple options:</p><h2>Delta Time</h2><p>Pop open any beginner Unity tutorial and they&#8217;ll tell you to make sure you&#8217;re multiplying all your speed values by a special value called &#8216;delta_time&#8217;. This is because the Unity&#8217;s default &#8216;Update&#8217; functionality is basically a loop like the one described above; it runs as fast as possible. To account for this, the engine keeps track of the delta_time value, and provides it to you. This is basically the time, in seconds, between when the last loop started and when it ended. By doing things like multiplying speed values by this number, you can have the game simulate things &#8220;consistently&#8221; even if it&#8217;s running at different speeds.</p><p>Let&#8217;s take a look at our previous example. I set my enemy&#8217;s speed; assuming I want it to move 50 units per second I would set the speed value per loop to be 50 multiplied by delta_time. Let&#8217;s take a look at how this play&#8217;s out on my vs. B-san&#8217;s PC:</p><ol><li><p>On my PC, the loop runs on average 500 times per second. This means delta_time usually about 0.002 (1/500). This means my enemy will move 0.1 units per loop (50x0.002), which, lo and behold, adds up to 50 over 500 loops.</p></li><li><p>On B-san&#8217;s PC, the loop runs 100 times per second, so delta_time is 0.01 (1/100). This means the enemy moves 0.5 units per loop (50x0.01) which, once again, adds up to 50 over 100 loops. Hooray!</p></li></ol><p>Anyway, using this sort of thing for any kind of physics code is idiotic. Sorry, unity tutorials.</p><p>Say that, as a completely sane person trying to prove a point, I throw my laptop in the oven. The fans can&#8217;t keep up, everything heats up, the CPU starts throttling and everything slows down drastically. My loop now runs only 2 times every second. The enemy starts moving 25 units per loop. Sure, in theory they&#8217;re moving the same speed as before, but now they&#8217;re snapping around, phasing through walls, the simulation starts breaking, the floors start melting, chaos. </p><p>The issue here is that, while this method smoothly can handle fluctuations in hardware speed, it won&#8217;t produce perfectly consistent results. This makes it fine (or even preferable, for reasons we&#8217;ll see later) for stuff like camera movement, visual effects, animations, etc. since you could, in-principle, throw all that stuff out at any given moment and just (mostly) rederive it from the state of the game world without causing anything but a slight visual hiccup. But for anything that will the game relies upon in subsequent loops (such the position of the player, enemies, timers that affect what state they&#8217;re in, etc.) even small variations will propagate and cause significant differences in the overall state of the game. So what&#8217;s the alternative?</p><h2>Fixed Update</h2><p>In our original example, the game ran at varying speeds, but if you were observing the game from loop iteration to loop iteration without caring about how fast each loop was running then everything would appear consistent (i.e. the enemy would move the same distance over 100 loops on both my and B-san&#8217;s PC, it would just take longer in real-time on B-san&#8217;s side). If we had a way to ensure each loop took the same amount of time no matter what, this would fix the problem.</p><p>Despite the dismal state of it&#8217;s (mostly unofficial) introductory material, the Unity engine itself is naturally aware of this situation and provides an alternative &#8216;FixedUpdate&#8217; functionality specifically for physics code. And Game Maker&#8217;s normal &#8216;Step&#8217; functionality just does this by default (and requires that you do some questionable fiddling to even get it to work in the other way). But how does it work?</p><p>Step one involves picking a target game speed, i.e. how many times per second you want the loop to run. You may have noticed that I&#8217;ve hesitated to call this game speed the &#8220;framerate&#8221; so far; many engines will do so simply because the visual framerate is a natural number to use as your game speed<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. However, many games use a fixed loop for physics tasks that isn&#8217;t tied to the visible framerate (for example, Minecraft servers typically run at a fixed 20 &#8216;ticks per second&#8217;, while the visual framerate can vary significantly). But if you want to keep things simple, 60 &#8220;fps&#8221; is a natural choice for this, since it lets you update the game then draw the updated result once per loop. It also results in a <em>fixed </em>delta time of about 16.67 milliseconds (1/60*1000).</p><p>Now that we have our target framerate and delta time, step two involves inserting a variable <em>delay </em>into the loop. Think of each loop as a block of 16.67 milliseconds that we need to fully occupy, we put all of the game tasks at the start of the loop then fill whatever time is left with a delay period. To do this, check the time at the start of the loop, run all the tasks for that loop, then check the time again. Subtract the time at the start with the latest time to get how long it took to do all the tasks, then subtract <em>that </em>result from the target delta time. This tells you how long you need to delay for. Going back to our original example, doing the tasks might take different amounts of time, about 2ms on my PC vs. 10ms on B-san&#8217;s PC, but this is all evened out by the added delay (14.67ms for me vs. 6.67ms for B-san). Hooray!</p><p>But&#8230; is it real hooray&#8230; this time&#8230; ??? ?</p><h2>Lag</h2><p>The big issue with using the fixed loop method for everything is <em>lag</em>. What happens when the time it takes to run tasks is higher than the target delta time? In cases like these, there&#8217;s unfortunately no way around slowing down the game. This feels pretty terrible even if the slowdown is minor, it makes the game unplayable on sufficiently weak hardware, and even on computers that can run the game fine most of the time, it can seriously damage the play experience during particularly performance-intensive periods. </p><p>It also means players with better hardware can&#8217;t take advantage of it to produce a visually smoother experience. This matters less for retro-style 2D games, but for 3D games or games that use 3D elements like models with interpolated animations or smooth cameras it&#8217;s a pretty silly limitation. </p><p>As we saw in the delta time section, this is not a limitation shared by the other method. Sure, the game might get choppier under worse circumstances, but it will remain decently playable under far worse conditions. This means that, unsurprisingly, best prescription for combatting the problem of lag and variable hardware speeds is an approach that combines both methods.</p><p>And that approach is&#8230;</p><p>Not something I really care to look into.</p><h2>Huh???</h2><p>Yeah, for the (retro-style 2D) games I&#8217;m making, a simple fixed loop approach is more than good enough. I might tweak things a little to account for slight differences in monitor framerates, but the overall approach will be the same. Also, while it&#8217;s possible that I&#8217;ll change my mind about this in the future, part of the point of making my own engine is to gain the ability to develop games where lag isn&#8217;t really an issue (on any modern device at least), so mitigation strategies become less essential.</p><p>Game loops are difficult to talk about comprehensively. Since they&#8217;re at the root of your program, they&#8217;re necessary from the beginning, but they&#8217;re also constantly changing alongside the rest of things. At a certain point, once the engine is in more of a finished state, I&#8217;ll probably make a part 2 detailing exactly what I chose to put in mine, but for now this should cover everything I had to consider starting out.</p><p>Well, that&#8217;s one thing down when it comes to really stress-testing this engine. Next week I&#8217;ll talk about the thing I&#8217;ve spent the better part of last month working on: the build system!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-c31">&lt;Previous post</a> | <a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-86a">Next post&gt;</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Many retro games ran at just about 60 loops per second for the same reason that the NTSC standard for analog TV, used in Japan and most of the Americas, runs at around either 30 or 60 fps: the 60hz AC power supply. Resources were limited at the time, so game speeds were tied to the speed of the hardware itself, which was tied to the speed of the power supply. However, PAL, the standard used in most of Europe and the rest of the world, was designed with a 50hz power supply in mind. This is why the &#8220;PAL&#8221; versions of old games often ran 20% slower than normal, developers could not be bothered to properly adjust for the difference in hardware speed.</p></div></div>]]></content:encoded></item><item><title><![CDATA[So, what *are* we calling these?]]></title><description><![CDATA[My tweet popped off]]></description><link>https://blog.massimogauthier.com/p/so-what-are-we-calling-these</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/so-what-are-we-calling-these</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Thu, 06 Jun 2024 19:24:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!lMeT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>About a week ago I tweeted this out:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lMeT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lMeT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 424w, https://substackcdn.com/image/fetch/$s_!lMeT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 848w, https://substackcdn.com/image/fetch/$s_!lMeT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 1272w, https://substackcdn.com/image/fetch/$s_!lMeT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lMeT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png" width="373" height="455.4526315789474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:812,&quot;width&quot;:665,&quot;resizeWidth&quot;:373,&quot;bytes&quot;:632891,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lMeT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 424w, https://substackcdn.com/image/fetch/$s_!lMeT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 848w, https://substackcdn.com/image/fetch/$s_!lMeT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 1272w, https://substackcdn.com/image/fetch/$s_!lMeT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe5670ee-bb1d-4c38-9bd8-06ccdc3546a6_665x812.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It spread, uh, maybe a bit further than I was expecting:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ahce!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ahce!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 424w, https://substackcdn.com/image/fetch/$s_!Ahce!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 848w, https://substackcdn.com/image/fetch/$s_!Ahce!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 1272w, https://substackcdn.com/image/fetch/$s_!Ahce!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ahce!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png" width="664" height="138" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:138,&quot;width&quot;:664,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5901,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ahce!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 424w, https://substackcdn.com/image/fetch/$s_!Ahce!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 848w, https://substackcdn.com/image/fetch/$s_!Ahce!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 1272w, https://substackcdn.com/image/fetch/$s_!Ahce!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564b8109-72a0-4b69-b2b3-d7e7f3c5a0c8_664x138.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>Now that I&#8217;ve had some time to mull over the reactions, I thought it might be fun to revisit them after presenting a more complete thesis.</p><p>It&#8217;s not unexpected that a lot of people&#8217;s default assumption was that I was trying to identify some kind of subgenre. Truthfully though, I didn&#8217;t have anything so definite in mind. In the replies to the original tweet, I included a list of characteristics which I picked out as the set of combined elements which I thought tied these games together in my mind:</p><blockquote><ul><li><p>Exploration-focused.</p></li><li><p>Very "free", open-ended traversal.</p></li><li><p>Very hands-off tutorialization.</p></li><li><p>Major content locked behind secret, optional, and often difficult puzzles.</p></li><li><p>"Layered" secrets, "endings" that aren't really endings.</p></li><li><p>Rich lore is present or heavily implied.</p></li><li><p>Game knowledge is a major part of progression, veteran players can breeze through off knowledge alone.</p></li><li><p>Minimal focus on action gameplay; action challenges, if any, can often be circumvented with tools/knowledge gained from exploration.</p></li><li><p>Often features small puzzle-solving elements separate from wider exploration.</p></li><li><p>Players often describe their experience with these games as deeply meaningful and/or mind-blowing.</p></li></ul></blockquote><p>I was pretty much just trying to draw a circle around games which share all of these characteristics; even at the time, &#8220;subgenre&#8221; didn&#8217;t really feel like an appropriate label for the category.</p><p>So, what was I even searching for? We&#8217;ll get to the snappy label in a second, but if I now had to summarize the core thing that these sorts of games have in common it would be this:</p><p><em>Each game involves the player mastering a <strong>bespoke</strong>, <strong>predictable</strong>,<strong> </strong></em>and <em><strong>systemic </strong>environment <strong>as a whole</strong>, primarily through the <strong>open-ended</strong></em> <em>and <strong>unguided </strong>gathering of <strong>knowledge</strong></em>.</p><p>As promised, let&#8217;s test this thesis by responding to some of the replies to the original tweet. I&#8217;m sure everyone&#8217;s put just as much thought into this important question.</p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JMj9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JMj9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 424w, https://substackcdn.com/image/fetch/$s_!JMj9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 848w, https://substackcdn.com/image/fetch/$s_!JMj9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 1272w, https://substackcdn.com/image/fetch/$s_!JMj9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JMj9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png" width="1336" height="1284" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1284,&quot;width&quot;:1336,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:359728,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JMj9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 424w, https://substackcdn.com/image/fetch/$s_!JMj9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 848w, https://substackcdn.com/image/fetch/$s_!JMj9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 1272w, https://substackcdn.com/image/fetch/$s_!JMj9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690b0cdb-5678-4faf-a53a-4f416f7eb59e_1336x1284.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Thanks guys.</p><p>On a meta note, while writing that tweet I do recall heeding a raspy, goblin-like voice saying &#8220;phrase it like thisss, it will bring more engagement!&#8221; so I suppose I set myself up for this. And the tweet probably did spread a lot further than it would have if I had instead made a rambling thread full of caveats and clarifications. Which is probably&#8230; good? Yeah. Probably.</p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PXt8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PXt8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 424w, https://substackcdn.com/image/fetch/$s_!PXt8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 848w, https://substackcdn.com/image/fetch/$s_!PXt8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 1272w, https://substackcdn.com/image/fetch/$s_!PXt8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PXt8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png" width="745" height="148" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:148,&quot;width&quot;:745,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19969,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PXt8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 424w, https://substackcdn.com/image/fetch/$s_!PXt8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 848w, https://substackcdn.com/image/fetch/$s_!PXt8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 1272w, https://substackcdn.com/image/fetch/$s_!PXt8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb32025e5-5ae1-4ea8-a262-e5dd4fc027fd_745x148.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vSpN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vSpN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 424w, https://substackcdn.com/image/fetch/$s_!vSpN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 848w, https://substackcdn.com/image/fetch/$s_!vSpN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 1272w, https://substackcdn.com/image/fetch/$s_!vSpN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vSpN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png" width="738" height="183" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:183,&quot;width&quot;:738,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28583,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vSpN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 424w, https://substackcdn.com/image/fetch/$s_!vSpN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 848w, https://substackcdn.com/image/fetch/$s_!vSpN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 1272w, https://substackcdn.com/image/fetch/$s_!vSpN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd648f2c-ad27-4385-b3cc-e928543524f8_738x183.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Yvib!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Yvib!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 424w, https://substackcdn.com/image/fetch/$s_!Yvib!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 848w, https://substackcdn.com/image/fetch/$s_!Yvib!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 1272w, https://substackcdn.com/image/fetch/$s_!Yvib!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Yvib!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png" width="734" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:734,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36927,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Yvib!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 424w, https://substackcdn.com/image/fetch/$s_!Yvib!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 848w, https://substackcdn.com/image/fetch/$s_!Yvib!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 1272w, https://substackcdn.com/image/fetch/$s_!Yvib!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa26bf7c4-f59c-4e98-a094-1c9fa687ae11_734x210.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>I feel this is a kneejerk reaction to certain people trying to make these kinds of categories more authoritative than they have any right to be, and I understand wanting to push back against that. But I hope I made it clear that there <em>is </em>something worth pointing to here, and not all efforts of this kind are pointless navel-gazing. As I touched on in a reply to grayfruit, I don&#8217;t think that we would be seeing the same level of experimentation, iteration, and ultimately <em>results </em>we see with metroidvanias if &#8220;metroidvania&#8221; wasn&#8217;t a coherent category with a lot of shared understanding around it. Without a cultural touchstone like this, quickly communicating what your game is trying to do (to collaborators, investors, audience, etc.) just becomes harder at every level (often we&#8217;re forced to rely on examples of existing games, and tough luck if they&#8217;re not really well known). </p><p>Having enough of a shared language that those unfamiliar can use to get an initial grip on things is valuable. As a younger medium, I think games have a lot of design space left to explore compared to stuff like literature or film, and I think a willingness to map things out will help us find foundational conventions, wisdom about what <em>truly </em>works, faster.</p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xPZG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xPZG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 424w, https://substackcdn.com/image/fetch/$s_!xPZG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 848w, https://substackcdn.com/image/fetch/$s_!xPZG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 1272w, https://substackcdn.com/image/fetch/$s_!xPZG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xPZG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png" width="736" height="267" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:267,&quot;width&quot;:736,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44140,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xPZG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 424w, https://substackcdn.com/image/fetch/$s_!xPZG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 848w, https://substackcdn.com/image/fetch/$s_!xPZG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 1272w, https://substackcdn.com/image/fetch/$s_!xPZG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4d07d3a-9e34-44ef-a966-f0ab4c85caf2_736x267.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6MmQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6MmQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 424w, https://substackcdn.com/image/fetch/$s_!6MmQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 848w, https://substackcdn.com/image/fetch/$s_!6MmQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 1272w, https://substackcdn.com/image/fetch/$s_!6MmQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6MmQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png" width="721" height="244" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:244,&quot;width&quot;:721,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43587,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6MmQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 424w, https://substackcdn.com/image/fetch/$s_!6MmQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 848w, https://substackcdn.com/image/fetch/$s_!6MmQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 1272w, https://substackcdn.com/image/fetch/$s_!6MmQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26b94174-d855-4cc5-8504-e5b294b7c620_721x244.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DSoY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DSoY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 424w, https://substackcdn.com/image/fetch/$s_!DSoY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 848w, https://substackcdn.com/image/fetch/$s_!DSoY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 1272w, https://substackcdn.com/image/fetch/$s_!DSoY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DSoY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png" width="667" height="125" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:125,&quot;width&quot;:667,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16662,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DSoY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 424w, https://substackcdn.com/image/fetch/$s_!DSoY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 848w, https://substackcdn.com/image/fetch/$s_!DSoY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 1272w, https://substackcdn.com/image/fetch/$s_!DSoY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0e59eb6-2b41-4284-8122-ec3dcb13188f_667x125.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CfAo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CfAo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 424w, https://substackcdn.com/image/fetch/$s_!CfAo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 848w, https://substackcdn.com/image/fetch/$s_!CfAo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 1272w, https://substackcdn.com/image/fetch/$s_!CfAo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CfAo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png" width="622" height="136" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:136,&quot;width&quot;:622,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17523,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CfAo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 424w, https://substackcdn.com/image/fetch/$s_!CfAo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 848w, https://substackcdn.com/image/fetch/$s_!CfAo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 1272w, https://substackcdn.com/image/fetch/$s_!CfAo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e742fd-7c6f-4c74-8215-58d3f5bd4a7f_622x136.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VERx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VERx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 424w, https://substackcdn.com/image/fetch/$s_!VERx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 848w, https://substackcdn.com/image/fetch/$s_!VERx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 1272w, https://substackcdn.com/image/fetch/$s_!VERx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VERx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png" width="728" height="173" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:173,&quot;width&quot;:728,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30485,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VERx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 424w, https://substackcdn.com/image/fetch/$s_!VERx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 848w, https://substackcdn.com/image/fetch/$s_!VERx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 1272w, https://substackcdn.com/image/fetch/$s_!VERx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb98c4aae-33a7-4de4-ba0c-3adfc4f84151_728x173.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-AkC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-AkC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 424w, https://substackcdn.com/image/fetch/$s_!-AkC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 848w, https://substackcdn.com/image/fetch/$s_!-AkC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 1272w, https://substackcdn.com/image/fetch/$s_!-AkC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-AkC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png" width="694" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:694,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-AkC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 424w, https://substackcdn.com/image/fetch/$s_!-AkC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 848w, https://substackcdn.com/image/fetch/$s_!-AkC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 1272w, https://substackcdn.com/image/fetch/$s_!-AkC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6107b64e-21db-4168-911a-8bd90acd20bd_694x153.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>(and many more along these lines)</p><p>I&#8217;m not sure how many of these people actually took time to look at the list of similarities, but they do have a bit of a point: on the surface, the games presented play very differently. This is most of the reason why I don&#8217;t think the categorization is quite a &#8220;subgenre&#8221;. The core idea can evidently be applied to many different gameplay styles, though it does seem to mesh best with puzzle games (in the &#8216;bite-sized puzzle&#8217; sense).</p><p>It does feel to me like other people are able to pick up on the shared heart of these games, given reactions like this:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2xEA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2xEA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 424w, https://substackcdn.com/image/fetch/$s_!2xEA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 848w, https://substackcdn.com/image/fetch/$s_!2xEA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 1272w, https://substackcdn.com/image/fetch/$s_!2xEA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2xEA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png" width="734" height="206" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:206,&quot;width&quot;:734,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:40083,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2xEA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 424w, https://substackcdn.com/image/fetch/$s_!2xEA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 848w, https://substackcdn.com/image/fetch/$s_!2xEA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 1272w, https://substackcdn.com/image/fetch/$s_!2xEA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01a9fb11-bf3b-4f9d-97f3-7ae814de6bbc_734x206.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And people managing to point out other examples that nail it:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m7ay!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m7ay!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 424w, https://substackcdn.com/image/fetch/$s_!m7ay!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 848w, https://substackcdn.com/image/fetch/$s_!m7ay!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 1272w, https://substackcdn.com/image/fetch/$s_!m7ay!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m7ay!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png" width="573" height="100" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:100,&quot;width&quot;:573,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!m7ay!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 424w, https://substackcdn.com/image/fetch/$s_!m7ay!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 848w, https://substackcdn.com/image/fetch/$s_!m7ay!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 1272w, https://substackcdn.com/image/fetch/$s_!m7ay!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2362882c-5edc-4cdb-8095-79e7c204d92f_573x100.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>But a wise man (who sleeps on my couch) once said you find what&#8217;s interesting about things by seeing how they <em>differ</em> from other things<em>, </em>rather than pointing out similarities. In that spirit, I did try to point out some games that I think fall just shy of this category and why:</p><blockquote><ul><li><p>Inscryption: Too linear, not enough 'free' exploration, too strategy focused.</p></li><li><p>Cave Story: Too linear, not enough exploration focus, progression is not so heavily based on game knowledge, too action-focused.</p></li><li><p>Paradise Killer: "Puzzle solving" is too straightforward, mostly a collect-a-thon.</p></li><li><p>Return of the Obra Dinn: Does not feature hidden "layers" of content, the method of exploration and discovery does not break much from expectations set at the start of the game.</p></li><li><p>Baba is You: Not particularly lore rich.</p></li><li><p>Stephen's Sausage Roll: Outright "hidden" content is minimal.</p></li><li><p>Undertale: Main game is too linear, "open" exploration elements are minimal.</p></li><li><p>The Stanley Parable: No real puzzle-solving to speak of.</p></li></ul></blockquote><p>Some other games people brought up also felt similar but ultimately separate to me, I&#8217;ll take the opportunity to go in-depth as to why:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ptb3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ptb3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 424w, https://substackcdn.com/image/fetch/$s_!Ptb3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 848w, https://substackcdn.com/image/fetch/$s_!Ptb3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 1272w, https://substackcdn.com/image/fetch/$s_!Ptb3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ptb3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png" width="687" height="198" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:198,&quot;width&quot;:687,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33574,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ptb3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 424w, https://substackcdn.com/image/fetch/$s_!Ptb3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 848w, https://substackcdn.com/image/fetch/$s_!Ptb3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 1272w, https://substackcdn.com/image/fetch/$s_!Ptb3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd30d8477-0cb0-40bf-9419-8e2560d3952c_687x198.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Dark Souls titles, while featuring quite a surprising number of traits from the original list, are fairly easy to exclude simply due to their segmentation and linearity. The world <em><strong>as a whole</strong> </em>is never turned into a puzzle for you to solve.</p><p>Elden Ring was admittedly harder to exclude; it <em>does </em>have a well-executed <em><strong>open-ended</strong></em> exploration aspect, and quite a pile of secrets and knowledge to collect. In its case, I think it&#8217;s simply a matter of where the focus lies. Progression in Elden Ring is not solely or even primarily based around <em><strong>knowledge</strong></em>,<em> </em>i.e. investigation, cracking the world&#8217;s puzzle, etc. RPG-style character building and the development of your action-based combat skills play a much larger role; this is ultimately what sets it apart from the original set of examples.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xaYH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xaYH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 424w, https://substackcdn.com/image/fetch/$s_!xaYH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 848w, https://substackcdn.com/image/fetch/$s_!xaYH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 1272w, https://substackcdn.com/image/fetch/$s_!xaYH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xaYH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png" width="748" height="207" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:207,&quot;width&quot;:748,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32405,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xaYH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 424w, https://substackcdn.com/image/fetch/$s_!xaYH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 848w, https://substackcdn.com/image/fetch/$s_!xaYH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 1272w, https://substackcdn.com/image/fetch/$s_!xaYH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a53430f-ede9-41c3-a2bb-6d64c166d8ad_748x207.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Well said gorillastats. Disco Elysium&#8230; it&#8217;s feels close. But despite all the freedom the player is handed, it&#8217;s all concentrated in how the moment-to-moment gameplay unfolds. In most playthroughs, all the same scenes will occur, even if their exact contents and order might be shuffled around (and give-or-take some optional questlines). And you&#8217;re never really <em>alone </em>in your journey to unravel the game&#8217;s mysteries, thanks to the more-or-less friendly NPCs you&#8217;re always talking to, as well as&#8230; the player character? </p><p>Yeah, that&#8217;s the other glaring thing, <em>you&#8217;re </em>not quite the one doing any puzzle solving, you&#8217;re <em>roleplaying as a character</em> that does so. There is no real <em><strong>systemic </strong></em>overarching environment to be analyzed and conquered, but rather a (skilfully woven) web of fiction the player gradually traces to its inevitable conclusion. That&#8217;s what sets Disco Elysium (and many other &#8220;adventure&#8221; games) apart.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QFML!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QFML!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 424w, https://substackcdn.com/image/fetch/$s_!QFML!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 848w, https://substackcdn.com/image/fetch/$s_!QFML!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 1272w, https://substackcdn.com/image/fetch/$s_!QFML!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QFML!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png" width="725" height="213" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:213,&quot;width&quot;:725,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25641,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QFML!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 424w, https://substackcdn.com/image/fetch/$s_!QFML!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 848w, https://substackcdn.com/image/fetch/$s_!QFML!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 1272w, https://substackcdn.com/image/fetch/$s_!QFML!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21d0233c-fcb6-45b0-9229-e28f5c1fea90_725x213.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Er, nowhere did I claim that games in this category are inherently better or worse than others, there are plenty of games on the &#8220;not&#8221; list that I like more than some out of the 7 original examples I gave. </p><p>Regardless, I wanted to bring up Obra Dinn since I think what distinguishes it is telling. It does seem to check a lot of boxes, so why doesn&#8217;t it fit? I think my response to the previous reply hints at what&#8217;s going on here. While Obra Dinn has something of an overarching system, deciphering it is not where the game&#8217;s challenge lies. All the important rules are freely presented to you fairly early on, and you are left with a very clear set of goals to accomplish. Heck, the game even hands you a comprehensive list of blank fields to be filled in as you go. </p><p>This is in contrast to the original example games, who are <em><strong>unguided</strong></em>;<em><strong> </strong></em>you&#8217;re always expected to fathom yourself not just the world and its history, but <em>the very rules of the game you&#8217;re playing</em>.<em> </em>Each discovery in this realm drastically shifts your perspective and recontextualizes your entire playthrough thus far, and the games are usually structured such that you come to these realizations at an interesting pace. These realizations, discoveries, perspective-shifts, one might even call them&#8230; epiphanies&#8230;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4fMg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4fMg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 424w, https://substackcdn.com/image/fetch/$s_!4fMg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 848w, https://substackcdn.com/image/fetch/$s_!4fMg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 1272w, https://substackcdn.com/image/fetch/$s_!4fMg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4fMg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png" width="738" height="218" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da52068c-64ef-42aa-a164-fbb0b4814774_738x218.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:218,&quot;width&quot;:738,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29822,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4fMg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 424w, https://substackcdn.com/image/fetch/$s_!4fMg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 848w, https://substackcdn.com/image/fetch/$s_!4fMg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 1272w, https://substackcdn.com/image/fetch/$s_!4fMg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda52068c-64ef-42aa-a164-fbb0b4814774_738x218.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>So, even I felt Rain World was a bit of an outlier in the original list, and now I think I can put my finger on why. It&#8217;s closer than any of the other examples of &#8220;almost&#8221; games I&#8217;ve presented thus far (obviously, since it managed to sneak onto the original list lol), but there&#8217;s one clear difference I can point to now between it and the other games. Rather than being entirely <strong>bespoke </strong>and <strong>predictable</strong>, its system is partially <em>generative </em>and <em>non-deterministic</em>.</p><p>Let&#8217;s use an example: in Outer Wilds, the developers (and eventually the player) can have complete knowledge of the solar system&#8217;s workings. Barring glitches and player interference, every celestial body will be in a known position at a any given time, every machine in the game will work the same way every time it&#8217;s used, and events and changes can be predicted with perfect accuracy. This means that any knowledge of the system that the player obtains is immediately and clearly applicable and reliable.</p><p>Conversely, in Rain World, a large and critical part of the game&#8217;s overarching &#8220;system&#8221; is its dynamic ecosystem. Animals all have their own AI will behave unpredictably in response to the player, the environment, and other animals. The ecosystem also exists and progresses independent of the player, which means the world and its inhabitants might undergo significant change while you&#8217;re not looking. While there are still rules to discover, significantly more experimentation might be necessary to do so, the knowledge earned is harder to apply reliably, and few things ever become truly 100% predictable.</p><p>Is this difference enough to disqualify rain world? Maybe not, I don&#8217;t think it&#8217;s necessarily satisfying a completely different internal drive, and it does feel <em>very </em>similar to the other examples. Still, there is a clear difference, and I think it might be worth further distinguishing these sorts of &#8220;emergent sim-y&#8221; takes on the core idea for those who can stomach the nuance.</p><div><hr></div><p> Alright, let&#8217;s see if anyone&#8217;s come up with a good, snappy tag for this sort of thing.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AgFC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AgFC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 424w, https://substackcdn.com/image/fetch/$s_!AgFC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 848w, https://substackcdn.com/image/fetch/$s_!AgFC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 1272w, https://substackcdn.com/image/fetch/$s_!AgFC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AgFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png" width="733" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:733,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35185,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AgFC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 424w, https://substackcdn.com/image/fetch/$s_!AgFC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 848w, https://substackcdn.com/image/fetch/$s_!AgFC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 1272w, https://substackcdn.com/image/fetch/$s_!AgFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ae95f2c-eef4-4813-be78-6bbac84d89ef_733x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DJ7V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DJ7V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 424w, https://substackcdn.com/image/fetch/$s_!DJ7V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 848w, https://substackcdn.com/image/fetch/$s_!DJ7V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 1272w, https://substackcdn.com/image/fetch/$s_!DJ7V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DJ7V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png" width="732" height="156" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:156,&quot;width&quot;:732,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25333,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DJ7V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 424w, https://substackcdn.com/image/fetch/$s_!DJ7V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 848w, https://substackcdn.com/image/fetch/$s_!DJ7V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 1272w, https://substackcdn.com/image/fetch/$s_!DJ7V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac48ac0d-2783-4d46-9e87-40b76203d0c2_732x156.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TBtu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TBtu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 424w, https://substackcdn.com/image/fetch/$s_!TBtu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 848w, https://substackcdn.com/image/fetch/$s_!TBtu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 1272w, https://substackcdn.com/image/fetch/$s_!TBtu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TBtu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png" width="743" height="115" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:115,&quot;width&quot;:743,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15700,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TBtu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 424w, https://substackcdn.com/image/fetch/$s_!TBtu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 848w, https://substackcdn.com/image/fetch/$s_!TBtu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 1272w, https://substackcdn.com/image/fetch/$s_!TBtu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59b00046-4f13-4eac-9d85-4d825c7637c1_743x115.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eCHL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eCHL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 424w, https://substackcdn.com/image/fetch/$s_!eCHL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 848w, https://substackcdn.com/image/fetch/$s_!eCHL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 1272w, https://substackcdn.com/image/fetch/$s_!eCHL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eCHL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png" width="734" height="122" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2032d162-be22-4a86-9900-fab442e83416_734x122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:122,&quot;width&quot;:734,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18068,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eCHL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 424w, https://substackcdn.com/image/fetch/$s_!eCHL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 848w, https://substackcdn.com/image/fetch/$s_!eCHL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 1272w, https://substackcdn.com/image/fetch/$s_!eCHL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2032d162-be22-4a86-9900-fab442e83416_734x122.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HK7i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HK7i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 424w, https://substackcdn.com/image/fetch/$s_!HK7i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 848w, https://substackcdn.com/image/fetch/$s_!HK7i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 1272w, https://substackcdn.com/image/fetch/$s_!HK7i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HK7i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png" width="736" height="120" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:120,&quot;width&quot;:736,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17135,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HK7i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 424w, https://substackcdn.com/image/fetch/$s_!HK7i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 848w, https://substackcdn.com/image/fetch/$s_!HK7i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 1272w, https://substackcdn.com/image/fetch/$s_!HK7i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a8c1c2-8d74-4785-874d-6fa88d25aef6_736x120.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!btBK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!btBK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 424w, https://substackcdn.com/image/fetch/$s_!btBK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 848w, https://substackcdn.com/image/fetch/$s_!btBK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 1272w, https://substackcdn.com/image/fetch/$s_!btBK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!btBK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png" width="741" height="126" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:126,&quot;width&quot;:741,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17565,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!btBK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 424w, https://substackcdn.com/image/fetch/$s_!btBK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 848w, https://substackcdn.com/image/fetch/$s_!btBK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 1272w, https://substackcdn.com/image/fetch/$s_!btBK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5a20ba8-0ac2-45e3-b69b-3fc3572069b2_741x126.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>I don&#8217;t really think sticking them under a broad umbrella like this is really helpful. For example, there are plenty of titles I and others would happily deem &#8220;adventure games&#8221; that clearly <em>don&#8217;t </em>fall in or even close to the category. Similarly broad labels don&#8217;t fare much better in this regard.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pgMb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pgMb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 424w, https://substackcdn.com/image/fetch/$s_!pgMb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 848w, https://substackcdn.com/image/fetch/$s_!pgMb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 1272w, https://substackcdn.com/image/fetch/$s_!pgMb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pgMb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png" width="738" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:738,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33867,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pgMb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 424w, https://substackcdn.com/image/fetch/$s_!pgMb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 848w, https://substackcdn.com/image/fetch/$s_!pgMb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 1272w, https://substackcdn.com/image/fetch/$s_!pgMb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc758fa8-8ff8-4fe9-bc09-2bd488d3cd1a_738x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">omg nightmargin</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4Swv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4Swv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 424w, https://substackcdn.com/image/fetch/$s_!4Swv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 848w, https://substackcdn.com/image/fetch/$s_!4Swv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 1272w, https://substackcdn.com/image/fetch/$s_!4Swv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4Swv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png" width="740" height="122" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80869819-b0c0-489c-9275-534049c92c69_740x122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:122,&quot;width&quot;:740,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15621,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4Swv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 424w, https://substackcdn.com/image/fetch/$s_!4Swv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 848w, https://substackcdn.com/image/fetch/$s_!4Swv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 1272w, https://substackcdn.com/image/fetch/$s_!4Swv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80869819-b0c0-489c-9275-534049c92c69_740x122.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7iuf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7iuf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 424w, https://substackcdn.com/image/fetch/$s_!7iuf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 848w, https://substackcdn.com/image/fetch/$s_!7iuf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 1272w, https://substackcdn.com/image/fetch/$s_!7iuf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7iuf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png" width="747" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f2affe2-8785-4433-8914-1083940bf58f_747x153.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:747,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:23006,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7iuf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 424w, https://substackcdn.com/image/fetch/$s_!7iuf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 848w, https://substackcdn.com/image/fetch/$s_!7iuf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 1272w, https://substackcdn.com/image/fetch/$s_!7iuf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2affe2-8785-4433-8914-1083940bf58f_747x153.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q--O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q--O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 424w, https://substackcdn.com/image/fetch/$s_!q--O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 848w, https://substackcdn.com/image/fetch/$s_!q--O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 1272w, https://substackcdn.com/image/fetch/$s_!q--O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q--O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png" width="733" height="451" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/daef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:451,&quot;width&quot;:733,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53885,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q--O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 424w, https://substackcdn.com/image/fetch/$s_!q--O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 848w, https://substackcdn.com/image/fetch/$s_!q--O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 1272w, https://substackcdn.com/image/fetch/$s_!q--O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaef0279-0a14-4ddc-b738-7fb1285efcb3_733x451.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I had some, ah, choice words for this one:</p><blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1m2g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1m2g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 424w, https://substackcdn.com/image/fetch/$s_!1m2g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 848w, https://substackcdn.com/image/fetch/$s_!1m2g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 1272w, https://substackcdn.com/image/fetch/$s_!1m2g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1m2g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png" width="737" height="150" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/adf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:150,&quot;width&quot;:737,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28249,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1m2g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 424w, https://substackcdn.com/image/fetch/$s_!1m2g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 848w, https://substackcdn.com/image/fetch/$s_!1m2g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 1272w, https://substackcdn.com/image/fetch/$s_!1m2g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadf6b47b-2460-4c67-8077-f0e562f195bf_737x150.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5Paf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5Paf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 424w, https://substackcdn.com/image/fetch/$s_!5Paf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 848w, https://substackcdn.com/image/fetch/$s_!5Paf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 1272w, https://substackcdn.com/image/fetch/$s_!5Paf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5Paf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png" width="735" height="187" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:187,&quot;width&quot;:735,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30876,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5Paf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 424w, https://substackcdn.com/image/fetch/$s_!5Paf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 848w, https://substackcdn.com/image/fetch/$s_!5Paf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 1272w, https://substackcdn.com/image/fetch/$s_!5Paf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbf1cd5d-7f9e-4a65-be37-2ede819030e7_735x187.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div></blockquote><p>Not much more to say, though to expand on the first comment, the reason I think an overreliance on genre terms based on existing titles is corrosive (doubly so for genre terms like this which are based on existing genre terms of the sort) is that it tends to obfuscate what we actually care about in those titles and makes it unnecessarily harder to communicate with those who might be less familiar with those titles. Some mentioned the term &#8220;tunicwild&#8221;, the same criticism applies to that.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!g_Hr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!g_Hr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 424w, https://substackcdn.com/image/fetch/$s_!g_Hr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 848w, https://substackcdn.com/image/fetch/$s_!g_Hr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 1272w, https://substackcdn.com/image/fetch/$s_!g_Hr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!g_Hr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png" width="737" height="147" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:147,&quot;width&quot;:737,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22714,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!g_Hr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 424w, https://substackcdn.com/image/fetch/$s_!g_Hr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 848w, https://substackcdn.com/image/fetch/$s_!g_Hr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 1272w, https://substackcdn.com/image/fetch/$s_!g_Hr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d0c8e2-872b-40f8-81c8-e31ebf486791_737x147.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QBWR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QBWR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 424w, https://substackcdn.com/image/fetch/$s_!QBWR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 848w, https://substackcdn.com/image/fetch/$s_!QBWR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 1272w, https://substackcdn.com/image/fetch/$s_!QBWR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QBWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png" width="741" height="182" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:182,&quot;width&quot;:741,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28914,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QBWR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 424w, https://substackcdn.com/image/fetch/$s_!QBWR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 848w, https://substackcdn.com/image/fetch/$s_!QBWR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 1272w, https://substackcdn.com/image/fetch/$s_!QBWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e78e647-2aa4-4552-9fa9-bc79b5f74f3a_741x182.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XudO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XudO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 424w, https://substackcdn.com/image/fetch/$s_!XudO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 848w, https://substackcdn.com/image/fetch/$s_!XudO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 1272w, https://substackcdn.com/image/fetch/$s_!XudO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XudO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png" width="734" height="147" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:147,&quot;width&quot;:734,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27414,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XudO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 424w, https://substackcdn.com/image/fetch/$s_!XudO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 848w, https://substackcdn.com/image/fetch/$s_!XudO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 1272w, https://substackcdn.com/image/fetch/$s_!XudO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F642b1613-ec24-4f1d-a65a-8baa4c3965c6_734x147.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QQ70!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QQ70!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 424w, https://substackcdn.com/image/fetch/$s_!QQ70!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 848w, https://substackcdn.com/image/fetch/$s_!QQ70!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 1272w, https://substackcdn.com/image/fetch/$s_!QQ70!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QQ70!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png" width="733" height="114" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c25258b8-8594-4edb-8075-a09a0518f66f_733x114.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:114,&quot;width&quot;:733,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16538,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QQ70!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 424w, https://substackcdn.com/image/fetch/$s_!QQ70!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 848w, https://substackcdn.com/image/fetch/$s_!QQ70!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 1272w, https://substackcdn.com/image/fetch/$s_!QQ70!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc25258b8-8594-4edb-8075-a09a0518f66f_733x114.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JjJe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JjJe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 424w, https://substackcdn.com/image/fetch/$s_!JjJe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 848w, https://substackcdn.com/image/fetch/$s_!JjJe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 1272w, https://substackcdn.com/image/fetch/$s_!JjJe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JjJe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png" width="733" height="145" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:145,&quot;width&quot;:733,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24595,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JjJe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 424w, https://substackcdn.com/image/fetch/$s_!JjJe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 848w, https://substackcdn.com/image/fetch/$s_!JjJe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 1272w, https://substackcdn.com/image/fetch/$s_!JjJe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35818e89-8406-416e-aff9-cb0097a1c03f_733x145.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mzk3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mzk3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 424w, https://substackcdn.com/image/fetch/$s_!mzk3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 848w, https://substackcdn.com/image/fetch/$s_!mzk3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 1272w, https://substackcdn.com/image/fetch/$s_!mzk3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mzk3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png" width="738" height="119" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:119,&quot;width&quot;:738,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13143,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mzk3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 424w, https://substackcdn.com/image/fetch/$s_!mzk3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 848w, https://substackcdn.com/image/fetch/$s_!mzk3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 1272w, https://substackcdn.com/image/fetch/$s_!mzk3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb9f21aa-cd9b-438c-b765-1c90a659c8ea_738x119.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fQCr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fQCr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 424w, https://substackcdn.com/image/fetch/$s_!fQCr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 848w, https://substackcdn.com/image/fetch/$s_!fQCr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 1272w, https://substackcdn.com/image/fetch/$s_!fQCr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fQCr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png" width="742" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:742,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29420,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fQCr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 424w, https://substackcdn.com/image/fetch/$s_!fQCr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 848w, https://substackcdn.com/image/fetch/$s_!fQCr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 1272w, https://substackcdn.com/image/fetch/$s_!fQCr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7f65b8-2b01-4d9d-8daf-8cfe43febc70_742x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PD81!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PD81!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 424w, https://substackcdn.com/image/fetch/$s_!PD81!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 848w, https://substackcdn.com/image/fetch/$s_!PD81!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 1272w, https://substackcdn.com/image/fetch/$s_!PD81!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PD81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png" width="753" height="124" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:124,&quot;width&quot;:753,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12335,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PD81!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 424w, https://substackcdn.com/image/fetch/$s_!PD81!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 848w, https://substackcdn.com/image/fetch/$s_!PD81!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 1272w, https://substackcdn.com/image/fetch/$s_!PD81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7fa4e19-8f03-447c-8d7a-255a5e7588e3_753x124.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Now we&#8217;re talking. I think all these monikers do a good job pointing at the specific thing that makes these games interesting. But as we saw in the Obra Dinn counterexample, &#8220;layers&#8221; is not quite at the <em>core </em>of this feeling. Can we do better?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U6lM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U6lM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 424w, https://substackcdn.com/image/fetch/$s_!U6lM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 848w, https://substackcdn.com/image/fetch/$s_!U6lM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 1272w, https://substackcdn.com/image/fetch/$s_!U6lM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U6lM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png" width="734" height="296" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:296,&quot;width&quot;:734,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86893,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!U6lM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 424w, https://substackcdn.com/image/fetch/$s_!U6lM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 848w, https://substackcdn.com/image/fetch/$s_!U6lM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 1272w, https://substackcdn.com/image/fetch/$s_!U6lM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbf68850-5f78-42ce-8b93-7b4f6af2b978_734x296.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bdEc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bdEc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 424w, https://substackcdn.com/image/fetch/$s_!bdEc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 848w, https://substackcdn.com/image/fetch/$s_!bdEc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 1272w, https://substackcdn.com/image/fetch/$s_!bdEc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bdEc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png" width="744" height="338" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:338,&quot;width&quot;:744,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:91798,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bdEc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 424w, https://substackcdn.com/image/fetch/$s_!bdEc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 848w, https://substackcdn.com/image/fetch/$s_!bdEc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 1272w, https://substackcdn.com/image/fetch/$s_!bdEc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2f7f452-7f3f-41f5-87d7-276c66efd60b_744x338.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dUwv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dUwv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 424w, https://substackcdn.com/image/fetch/$s_!dUwv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 848w, https://substackcdn.com/image/fetch/$s_!dUwv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 1272w, https://substackcdn.com/image/fetch/$s_!dUwv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dUwv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png" width="748" height="408" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52603f7e-4411-47d5-a98c-423edde112b9_748x408.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:408,&quot;width&quot;:748,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:72019,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dUwv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 424w, https://substackcdn.com/image/fetch/$s_!dUwv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 848w, https://substackcdn.com/image/fetch/$s_!dUwv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 1272w, https://substackcdn.com/image/fetch/$s_!dUwv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52603f7e-4411-47d5-a98c-423edde112b9_748x408.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;m pleasantly surprised people kept recommending that video, since it seems to frame &#8216;KBUs&#8217; as just a mechanic that some games have, rather than any sort of genre tag. It&#8217;s definitely on the right track in that regard, and, as we saw, knowledge-based progression is crucial to the category definition. Even the Hypnospace designer seems to agree. Unfortunately, this term seems too narrow, and it&#8217;s pointing to a mechanic that many games have, not just the ones we&#8217;re trying to delineate.</p><p>Consciously or not though, it seems Jay might have independently hit upon what we&#8217;re looking for in his reply&#8230;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ixN2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ixN2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 424w, https://substackcdn.com/image/fetch/$s_!ixN2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 848w, https://substackcdn.com/image/fetch/$s_!ixN2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 1272w, https://substackcdn.com/image/fetch/$s_!ixN2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ixN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png" width="750" height="143" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:143,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19293,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ixN2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 424w, https://substackcdn.com/image/fetch/$s_!ixN2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 848w, https://substackcdn.com/image/fetch/$s_!ixN2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 1272w, https://substackcdn.com/image/fetch/$s_!ixN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5ed660-3754-418f-8e33-a247b6c4e168_750x143.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E3LC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E3LC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 424w, https://substackcdn.com/image/fetch/$s_!E3LC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 848w, https://substackcdn.com/image/fetch/$s_!E3LC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 1272w, https://substackcdn.com/image/fetch/$s_!E3LC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E3LC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png" width="754" height="201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:201,&quot;width&quot;:754,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:42773,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E3LC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 424w, https://substackcdn.com/image/fetch/$s_!E3LC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 848w, https://substackcdn.com/image/fetch/$s_!E3LC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 1272w, https://substackcdn.com/image/fetch/$s_!E3LC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15dd3ddd-228d-46ea-96df-45dd5b5898a1_754x201.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!adni!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!adni!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 424w, https://substackcdn.com/image/fetch/$s_!adni!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 848w, https://substackcdn.com/image/fetch/$s_!adni!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 1272w, https://substackcdn.com/image/fetch/$s_!adni!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!adni!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png" width="874" height="413" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:413,&quot;width&quot;:874,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:117492,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!adni!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 424w, https://substackcdn.com/image/fetch/$s_!adni!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 848w, https://substackcdn.com/image/fetch/$s_!adni!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 1272w, https://substackcdn.com/image/fetch/$s_!adni!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27148da5-6c8a-4514-8e68-9a4c3c2400da_874x413.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://www.gamedeveloper.com/design/-i-the-witness-i-modeling-epiphany">https://www.gamedeveloper.com/design/-i-the-witness-i-modeling-epiphany</a></figcaption></figure></div><p>This feeling. These might not be the only sorts of games that can provide it, but I can&#8217;t think of any type of game that provides a stronger or purer form of it than this one. If one lacks time to get into the nuance, I think &#8216;epiphany-driven&#8217; is great shorthand. </p><p>Good job, team!</p><div><hr></div><p>So&#8230; was there anything else? Well, some people took my original 7 examples as being <em>comprehensive, </em>which is pretty laughable. Therefore, I&#8217;d be remiss not to offer some game recs! Let&#8217;s see what the public has to say:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FZo1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FZo1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 424w, https://substackcdn.com/image/fetch/$s_!FZo1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 848w, https://substackcdn.com/image/fetch/$s_!FZo1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!FZo1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FZo1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png" width="1200" height="1200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1200,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:271828,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FZo1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 424w, https://substackcdn.com/image/fetch/$s_!FZo1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 848w, https://substackcdn.com/image/fetch/$s_!FZo1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!FZo1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636af2e8-6b4d-4c15-b41a-6654c9bf1b69_1200x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Yes, yes, ok!</em> I can&#8217;t <em>believe </em>I forgot Tunic when I was making the original image. It might be a good bit more action-focused than the other titles, but it definitely fits the bill.</p><p>Regarding Fez, it&#8217;s been a while since I last played it so I wasn&#8217;t sure how closely it fit. Still, I wouldn&#8217;t expect anyone who likes this sort of stuff to have a bad time with it, so go check it out!</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z10r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z10r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 424w, https://substackcdn.com/image/fetch/$s_!z10r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 848w, https://substackcdn.com/image/fetch/$s_!z10r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 1272w, https://substackcdn.com/image/fetch/$s_!z10r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z10r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png" width="750" height="161" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4f48800-f161-4198-b494-363330b2a57a_750x161.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:161,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26858,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z10r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 424w, https://substackcdn.com/image/fetch/$s_!z10r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 848w, https://substackcdn.com/image/fetch/$s_!z10r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 1272w, https://substackcdn.com/image/fetch/$s_!z10r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4f48800-f161-4198-b494-363330b2a57a_750x161.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Seconded. Solid recommendation. It&#8217;s similar to Baba is You in that it checks pretty much all the boxes but doesn&#8217;t quite have much in the way of lore for you to uncover. That aspect of things adds quite significantly to the feel and character of an epiphany-driven game, but at this point I won&#8217;t go quite so far as to call it absolutely essential.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!05-I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!05-I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 424w, https://substackcdn.com/image/fetch/$s_!05-I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 848w, https://substackcdn.com/image/fetch/$s_!05-I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 1272w, https://substackcdn.com/image/fetch/$s_!05-I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!05-I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png" width="746" height="183" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:183,&quot;width&quot;:746,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!05-I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 424w, https://substackcdn.com/image/fetch/$s_!05-I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 848w, https://substackcdn.com/image/fetch/$s_!05-I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 1272w, https://substackcdn.com/image/fetch/$s_!05-I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730327ef-b441-48f0-a739-f7a5fa5549f3_746x183.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Neither of these games quite land in the epiphany-driven bucket imo, but it&#8217;s interesting that Subnautica came up in my discussion of the topic with friends. If you want a not-quite-epiphany-driven game that leans more in a survival-sandbox direction, it might be the one for you.</p><div><hr></div><p>Also, here are all the games that look interesting from the trailers but which I haven&#8217;t otherwise played or heard much about. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4ogo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4ogo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 424w, https://substackcdn.com/image/fetch/$s_!4ogo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 848w, https://substackcdn.com/image/fetch/$s_!4ogo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 1272w, https://substackcdn.com/image/fetch/$s_!4ogo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4ogo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png" width="745" height="180" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b95380-9183-47ce-8e00-e996a95173f3_745x180.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:180,&quot;width&quot;:745,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17988,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4ogo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 424w, https://substackcdn.com/image/fetch/$s_!4ogo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 848w, https://substackcdn.com/image/fetch/$s_!4ogo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 1272w, https://substackcdn.com/image/fetch/$s_!4ogo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b95380-9183-47ce-8e00-e996a95173f3_745x180.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HfkI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HfkI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 424w, https://substackcdn.com/image/fetch/$s_!HfkI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 848w, https://substackcdn.com/image/fetch/$s_!HfkI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 1272w, https://substackcdn.com/image/fetch/$s_!HfkI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HfkI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png" width="740" height="184" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:184,&quot;width&quot;:740,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26344,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HfkI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 424w, https://substackcdn.com/image/fetch/$s_!HfkI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 848w, https://substackcdn.com/image/fetch/$s_!HfkI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 1272w, https://substackcdn.com/image/fetch/$s_!HfkI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1633bc7-2ec0-4cb4-8222-0c4ff4163477_740x184.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">I did play Monster&#8217;s Expedition. Not a huge fan, maybe I should give it another shot though.</figcaption></figure></div><p>That&#8217;s all, but if you have recommendations of your own, comment below!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-programmers: Entities! Components! ...System?]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-c31</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-c31</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Fri, 24 May 2024 11:56:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/584b37ca-55a9-49f9-8303-271e12091811_589x958.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non-901">&lt;Previous post</a> | </em><a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-bd8">Next post&gt;</a></p><div><hr></div><p>At this point, I&#8217;ve got all my tools set up; you might expect me to start talking about drawing stuff to the screen, or collecting the player&#8217;s input. But I already made my test app! I know I can do all that! I wanted to kick off the <em>real </em>engine project by building one of the many systems that would&#8217;ve been too much work for a test app, and what better place to start than the engine&#8217;s beating heart: the Entity system!</p><h2>Do I know what this is?</h2><p>Maybe. If you&#8217;ve ever used an engine like Unity or Unreal before, you&#8217;ll be familiar with &#8216;Entities&#8217; (a.k.a. &#8216;GameObjects&#8217; or &#8216;Actors&#8217;) in this sense. It&#8217;s an extremely common design pattern that many game engines use to organize and represent almost everything in the game world. My own system takes inspiration from those, while mixing in some ideas from Game Maker. I&#8217;ll tell you exactly how it works later, but before we talk about the &#8220;how&#8221;&#8230;</p><h2>Why do this?</h2><p>Let&#8217;s talk a bit about software architecture. There are a great many ways to think about it, but in this case it&#8217;s helpful to imagine it as a frame, a scaffold. What I&#8217;m doing here is building a core part of the thing that will make it easy to gradually construct a game by adding and tweaking parts while keeping the overall program smooth and performant.</p><p>Philosophies when it comes to this stuff are almost as numerous as the arguments surrounding them. To give you an idea of what I mean, here are a few &#8216;programming paradigms&#8217; that one can follow:</p><ul><li><p><strong>Imperative and Declarative Programming</strong>: These are sort of ur-paradigms, and most other paradigms will fall under either umbrella:</p><ul><li><p>Imperative programming centers around the idea of <em>telling </em>the computer what to do, usually as a list of instructions, which, as we saw in the previous post, is pretty close to how things work under the hood. C is the classic example of an imperative language.</p></li><li><p>Declarative programming is a bit stranger; the idea is that you&#8217;re <em>describing</em>, very precisely, your program&#8217;s problem domain, in the same way that one would &#8220;declare&#8221; that 1+1=2. Instead of incanting &#8220;do this, then this, then that!&#8221;, you incant &#8220;given this, it is so!&#8221;. &#8220;Running&#8221; your program is then just a matter of solving the equation with the given parameters. This is closer to pure mathematics than engineering, and this style tends to be a significantly less popular, but some declarative ideas have been enormously influential, even for imperative work. Lisp is the classic example of a declarative language.</p></li></ul></li><li><p><strong>Object-Oriented</strong> <strong>Programming (OOP)</strong>: An incredibly popular imperative style. Due to how, er, <em>storied </em>its history is, it&#8217;s a bit difficult to nail down exactly how people use this term nowadays, but the basic idea is that your code is divided up into &#8216;objects&#8217;. Each object has data, as well as &#8216;methods&#8217; (little bits of code that can be called to tell the object to do something). There&#8217;s a lot of neat stuff you can do with objects, but OOP has been criticized for encouraging programmers to come up with complicated object structures when a simple list of instructions would do fine. Java, C#, C++, Python, and many more popular languages are considered Object-Oriented.</p></li><li><p><strong>Functional Programming</strong>: A style of declarative programming that has seen some (relative) success. The style is based around <strong>functions</strong>, specifically &#8216;pure&#8217; functions. A function is just a bit of code that takes an input and produces an output, but the idea of a &#8216;pure&#8217; function is that it doesn&#8217;t &#8216;remember&#8217; anything, it can&#8217;t read or write to any memory outside of itself. This guarantees that the function will <em>always</em> produce the same output if given the same input, and that it won&#8217;t silently affect any part of my program without my knowledge. A <em>purely </em>functional style constructs a whole program entirely out of pure functions, a very declarative way of doing things, but pure functions can still be very valuable as part of an imperative program and many ideas from Functional programming have spread all over the place. Haskell is probably the most well-known functional language.</p></li><li><p><strong>Data-Oriented Programming </strong>(a.k.a. Data-Oriented Design, DOD): An imperative philosophy that has gained popularity recently in response to perceived failures of the OOP style. The Data-Oriented philosophy encourages programmers to think more about hardware and memory (see the previous post for more details about this), and to envision their programs as machines purpose-built for funneling and processing large amounts of data in an efficient manner. Jai and ODIN are very much being built under this paradigm. To give a concrete example of DOD, say I&#8217;m making my little game with bouncy balls and whatnot:</p><ul><li><p>If I were doing things OOP-style, I might conjure up a &#8220;ball&#8221; object, give it all the data and behaviors a ball would need, then stick a new one wherever whenever I needed it. When I want all the balls to do something, I&#8217;d define that behavior on the object, then go looking for each of them and tell them &#8220;do this thing you know how to do&#8221;.</p></li><li><p>If I were thinking of things from a DOD perspective, I&#8217;d say: &#8220;I&#8217;m gonna need about 100 bouncy balls!&#8221;. I&#8217;d figure out the exact way to lay out all the data about 100 balls would need, and bundle it in a way that makes it easy for me <em>and </em>the CPU to access. When I wanted the balls to do something, I&#8217;d make a big ol&#8217; &#8220;process_balls&#8221; function that would churn through all that data and make it behave like about 100 bouncy balls.</p></li></ul></li></ul><p>Personally, I always try to do what makes sense for the task at hand, and my Entity system is designed to offer me the flexibility to do so. As the core around which most game features will be built, this is, uh, a pretty important thing for it to have.</p><h2>And now, the how</h2><p>To understand the reasoning behind the design of my engine&#8217;s Entity system, let&#8217;s take a look at how it&#8217;s design compares to the way I&#8217;d typically do things in Game Maker, with the help of some familiar faces from ANTONBLAST!</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BQls!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BQls!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 424w, https://substackcdn.com/image/fetch/$s_!BQls!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 848w, https://substackcdn.com/image/fetch/$s_!BQls!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 1272w, https://substackcdn.com/image/fetch/$s_!BQls!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BQls!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png" width="264" height="225" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2058e118-2972-467d-a37e-9ac53fd21265_264x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:225,&quot;width&quot;:264,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1379,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BQls!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 424w, https://substackcdn.com/image/fetch/$s_!BQls!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 848w, https://substackcdn.com/image/fetch/$s_!BQls!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 1272w, https://substackcdn.com/image/fetch/$s_!BQls!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2058e118-2972-467d-a37e-9ac53fd21265_264x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">hi pippo</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Ia1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Ia1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 424w, https://substackcdn.com/image/fetch/$s_!_Ia1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 848w, https://substackcdn.com/image/fetch/$s_!_Ia1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 1272w, https://substackcdn.com/image/fetch/$s_!_Ia1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Ia1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png" width="271" height="257.26933333333335" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:356,&quot;width&quot;:375,&quot;resizeWidth&quot;:271,&quot;bytes&quot;:2450,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_Ia1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 424w, https://substackcdn.com/image/fetch/$s_!_Ia1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 848w, https://substackcdn.com/image/fetch/$s_!_Ia1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 1272w, https://substackcdn.com/image/fetch/$s_!_Ia1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec086659-598b-4bc9-b2cf-7d12bf21a476_375x356.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">It&#8217;s ballbuster!</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2EKj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2EKj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 424w, https://substackcdn.com/image/fetch/$s_!2EKj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 848w, https://substackcdn.com/image/fetch/$s_!2EKj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 1272w, https://substackcdn.com/image/fetch/$s_!2EKj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2EKj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png" width="284" height="243.24663677130044" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:382,&quot;width&quot;:446,&quot;resizeWidth&quot;:284,&quot;bytes&quot;:3413,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2EKj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 424w, https://substackcdn.com/image/fetch/$s_!2EKj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 848w, https://substackcdn.com/image/fetch/$s_!2EKj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 1272w, https://substackcdn.com/image/fetch/$s_!2EKj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bdd81f5-8617-4ad4-a793-fad674fe41fc_446x382.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Mosquito. His true name is under strict NDA. Which is good, because I forgot what it was.</figcaption></figure></div><p>GM tends to push the user into an OOP way of doing things. You create &#8220;Objects&#8221;, and each has events that get triggered when something happens (my own system uses something very similar in this regard, so we&#8217;ll talk about it later). You&#8217;re also encouraged to use a very OOP technique for code reuse: Inheritance.</p><p>How a paradigm handles code reuse is core to its identity, one might even say its in a sense what defines a paradigm. Having to repeat yourself is time-consuming and tedious, so programmers are always finding ways to avoid it. </p><p>A classic example is functions themselves. I described them as bits of code that take an input and produce an output, but their real power is that you can &#8216;call&#8217; them from anywhere, over and over, with just one line of code per call. If I have a complicated bit of behavior that I need to use often, I can put it into a function, then just call that function. If it takes no input, its practically as if I just copy-pasted that bit of code all over the place (but I can change every instance of it just by changing the function!). If it <em>does </em>take input, even better! I can make the function do something slightly different whenever I use it just by changing its input.</p><p>So, inheritance, what is it? Basically, it&#8217;s a way for objects to share functionality amongst themselves by &#8220;inheriting&#8221; it from a parent object. Confused? Let&#8217;s take the example of our three buddies<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>: </p><ul><li><p>Pippo is a simple enemy that walks around. The player can bounce on him to knock him over, and can also hit him to send him flying.</p></li><li><p>Ballbuster is a more dangerous enemy that also walks around. When he sees the player, he&#8217;ll charge at him. Like Pippo, he&#8217;ll go flying when the player hits him.</p></li><li><p>Mosquito doesn&#8217;t do much, just floats in place, but the player can also hit him and bounce off of him.</p></li></ul><p>You&#8217;ll notice that these are all enemies, and they all have behaviors in common. Instead of rewriting those common behaviors three times, I can put all the methods and data needed for them into a parent &#8220;Enemy&#8221; object, and have objects for each of the three enemies that inherit from it (any behavior specific to an individual enemy gets put in that individual enemy&#8217;s object).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dUua!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dUua!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 424w, https://substackcdn.com/image/fetch/$s_!dUua!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 848w, https://substackcdn.com/image/fetch/$s_!dUua!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 1272w, https://substackcdn.com/image/fetch/$s_!dUua!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dUua!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png" width="490" height="466.53256704980845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:497,&quot;width&quot;:522,&quot;resizeWidth&quot;:490,&quot;bytes&quot;:17326,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dUua!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 424w, https://substackcdn.com/image/fetch/$s_!dUua!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 848w, https://substackcdn.com/image/fetch/$s_!dUua!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 1272w, https://substackcdn.com/image/fetch/$s_!dUua!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd5ec2bc-17c8-4fb8-bf1f-da3d3ab91ca3_522x497.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>But wait! Pippo and Ballbuster walk around, while the mosquito just floats in place! Where do I put the walking behavior? Well, inheritance trees can go more than one layer deep, so we can do something like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kadY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kadY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 424w, https://substackcdn.com/image/fetch/$s_!kadY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 848w, https://substackcdn.com/image/fetch/$s_!kadY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 1272w, https://substackcdn.com/image/fetch/$s_!kadY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kadY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png" width="496" height="454.40506329113924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11556251-7715-4810-a834-380d42276071_632x579.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:579,&quot;width&quot;:632,&quot;resizeWidth&quot;:496,&quot;bytes&quot;:25839,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kadY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 424w, https://substackcdn.com/image/fetch/$s_!kadY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 848w, https://substackcdn.com/image/fetch/$s_!kadY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 1272w, https://substackcdn.com/image/fetch/$s_!kadY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11556251-7715-4810-a834-380d42276071_632x579.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And now, say I want as version of Pippo that&#8217;s mostly identical, but who sheds feathers when he gets hit? Instead of copying Pippo somewhere, I can just keep extending things!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2ozz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2ozz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 424w, https://substackcdn.com/image/fetch/$s_!2ozz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 848w, https://substackcdn.com/image/fetch/$s_!2ozz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 1272w, https://substackcdn.com/image/fetch/$s_!2ozz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2ozz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png" width="430" height="474.935691318328" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:687,&quot;width&quot;:622,&quot;resizeWidth&quot;:430,&quot;bytes&quot;:29603,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2ozz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 424w, https://substackcdn.com/image/fetch/$s_!2ozz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 848w, https://substackcdn.com/image/fetch/$s_!2ozz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 1272w, https://substackcdn.com/image/fetch/$s_!2ozz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7eb9a3d-0a16-40e1-815c-c112cde22fa1_622x687.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As you can see, objects and inheritance trees can be pretty powerful, and a natural way of modeling game mechanics like enemies, but as a project&#8217;s size and complexity starts to grow, they can run into some problems. Take this guy, for example:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WF-z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WF-z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 424w, https://substackcdn.com/image/fetch/$s_!WF-z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 848w, https://substackcdn.com/image/fetch/$s_!WF-z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 1272w, https://substackcdn.com/image/fetch/$s_!WF-z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WF-z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png" width="323" height="325.42857142857144" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:402,&quot;width&quot;:399,&quot;resizeWidth&quot;:323,&quot;bytes&quot;:3628,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WF-z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 424w, https://substackcdn.com/image/fetch/$s_!WF-z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 848w, https://substackcdn.com/image/fetch/$s_!WF-z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 1272w, https://substackcdn.com/image/fetch/$s_!WF-z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c4456-fd2b-4bd2-adc7-e0d161f9604e_399x402.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>He&#8217;s a big totem guy that the player can stand on top of. He spits stuff. Nice guy. Now, question for you, is this guy a <em>block</em>, or an <em>enemy</em>? Stuff will collide with him, just like a block, but he also attacks the player, and can be stunned, just like an enemy. You can&#8217;t inherit from multiple parents (for complicated technical reasons), so you kind of have to pick one.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OtEG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OtEG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 424w, https://substackcdn.com/image/fetch/$s_!OtEG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 848w, https://substackcdn.com/image/fetch/$s_!OtEG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 1272w, https://substackcdn.com/image/fetch/$s_!OtEG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OtEG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png" width="620" height="345.91939546599497" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:443,&quot;width&quot;:794,&quot;resizeWidth&quot;:620,&quot;bytes&quot;:19996,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OtEG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 424w, https://substackcdn.com/image/fetch/$s_!OtEG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 848w, https://substackcdn.com/image/fetch/$s_!OtEG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 1272w, https://substackcdn.com/image/fetch/$s_!OtEG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0153620-cc37-445e-b2bf-a27cb84623f6_794x443.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Usually, in cases like these, I have to pick the most relevant parent, and cobble together the other behavior as best I can. The other option is to rethink the inheritance tree structure, but rearranging the tree can be time-consuming and annoying, and will often cause problems for other objects. </p><p>Here are some other issues that crop up with inheritance:</p><ul><li><p>Say I have 10 objects. A handful of them want behavior A, a slightly different handful want behavior B, and another handful also want behavior C. Even if I&#8217;m aware of all this ahead of time, which I&#8217;m likely <em>not</em>, there isn&#8217;t an obvious way to make an inheritance tree that sorts the behaviors in a neat way. Making a parent with all three behaviors is doable, but would require shutting off certain behaviors in many of the children, which is annoying to do and keep track of, especially if it has to be done after-the-fact.</p></li><li><p>If a bunch of objects all want behaviors A, B, and C, but a few only want behaviors A and B, well, tough luck, they&#8217;re also getting C. I can turn it off, but this often still introduces complexity and performance overhead to objects for behaviors they aren&#8217;t even using.</p></li><li><p>Methods on parents can be inherited or &#8216;overriden&#8217; by child objects, which involves the child either replacing or adding on to what a method does. This is an important and useful feature, but it introduces performance overhead related to having to retrieve all these parent and child methods while the game is running.</p></li><li><p>Once inheritance trees start to reach a certain depth they get&#8230; annoying. Did I put that behavior in this object or that one? Should this new thing be a child of this or should I put it further down? I need to do all these things in order on this object but skip step 3 on this other object, do I split steps 1 and 2 into their own method? Oops, it&#8217;s now 3 months later and I don&#8217;t remember which part of the sequence is in which method, <em>fun</em>&#8230;</p></li></ul><p>Ok, so&#8230; what, then? This design pattern, built directly into almost every major modern language as a first class feature, used by most software out there, present in nearly every tutorial, is it just dumb and bad?</p><p>Uh, yeah, kinda.</p><p>I&#8217;m not the only one who thinks this, a lot of engineers have been distancing themselves from the excesses of OOP over the past decade and more, despite its ubiquity. But what&#8217;s the alternative? Personally, I&#8217;m not quite ready to jump fully off the OOP train, but if I&#8217;m gonna be avoiding inheritance for the most part, how am I gonna handle code reuse? This is where a compositional approach comes in.</p><h2>Go on then</h2><p>Let&#8217;s take a look, finally, at how my Entity system works:</p><ul><li><p>Everything in the world is represented by an <strong>Entity</strong>. An Entity can represent anything: the player, a tree, an enemy, the sky, etc.</p></li><li><p>On their own, Entities do nothing, <em>are </em>nothing. Their existence is defined by the set of <strong>Components </strong>attached to them.</p></li><li><p>Components are little bundles of data that can be combined together modularly to form an entity. Components can be many different things. They might be, for example:</p><ul><li><p>A set of coordinates that tells you where an Entity is in the world.</p></li><li><p>A set of physics parameters, like gravity and friction, which can be used to move an Entity around.</p></li><li><p>A set of rendering parameters, used to render an animated sprite.</p></li></ul></li><li><p>So, to give a complete example, say I wanted to make an Entity that represented a gremlin enemy. I&#8217;d attach a position and movement Component, so that I can move it around; a collider Component, so that it can collide with things; a hitbox Component, so that it can hit things with attacks; and I&#8217;d make a special new &#8220;gremlin&#8221; Component that would be used for anything specific to the gremlin.</p></li><li><p>In addition to all that, Components can react to a standardized set of <strong>Events, </strong>just like Game Maker objects.<strong> </strong>The most common Events would be `init`, which is triggered just for a specific Component when it is newly added to an Entity, and `update`, which is triggered once per frame<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>. Put simply, I can define behavior for each specific component that will trigger in response to these events.</p></li></ul><p>For the most part, this preserves a lot of what I like about Game Maker&#8217;s system while getting around the pitfalls of inheritance. Rather than bundling behaviors into parents, behaviors can be modularized into their own component and freely included or excluded depending on what a specific Entity needs. Take the totem guy from earlier: instead of agonizing over whether he should be an enemy or a block, I can just slap a bounce component and a block collision component on him and call it a day!<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><p>The way the component system is designed under the hood also has performance benefits. Rather than going to each entity, scattered around in memory willy-nilly, and tracking down their methods, every component of a given type is kept in one block which the engine processes all at once. Since components are usually pretty small, processing them all in one chunk like this can be extremely quick. &#8220;Entities&#8221;, under the hood, are actually just like little address books that components sometimes use to get related components from a different chunk. Neat!</p><h2>Hmm&#8230; still doesn&#8217;t seem very DOD</h2><p>Well&#8230; yeah. If you were paying attention to the bouncy ball example, I&#8217;ll admit this isn&#8217;t <em>quite </em>that. I&#8217;m making a bit of a compromise&#8230; but hear me out. A lot of my work days look like me:</p><ol><li><p>Getting a design for a new enemy, or block, or level mechanism.</p></li><li><p>Spinning up a new object that inherits from the relevant parent.</p></li><li><p>Writing up any new behaviors that object might need.</p></li><li><p>Revising and polishing that specific object.</p></li></ol><p>And I haven&#8217;t quite found a way to map that workflow, in my head, to something that doesn&#8217;t end up looking at least a little bit OOP. The Entity system, combined with a dash of code generation and some (perfectly kosher) syntactic tricks, allows me to quickly spin up work environments that feel just like what I&#8217;m used to, but better.</p><p>But, and here&#8217;s the beauty of making my own engine, I don&#8217;t <em>have </em>to use the entity system for everything. If a new mechanic or system seems like it would benefit from a less OOP-like approach, there&#8217;s nothing stopping me from taking it in that case. So&#8230; we&#8217;ll see. Maybe, at the end of a long project with this engine, I&#8217;ll find that I hardly needed any Entities at all. But for now, I&#8217;d like to have some familiar ground.</p><h2>Anything else?</h2><p>Not really, though I&#8217;d be remiss to mention one last thing. You might have heard of &#8220;Entity Component System&#8221; architectures, or ECS. Despite the name, my thing is&#8230; not that. Rather than being Event-driven like mine, entities in an ECS system use things called &#8220;Systems&#8221; (brilliant nomenclature, I know), which are these things which identify entities which have certain <em>sets </em>of components on them and run code on them. It&#8217;s not a bad idea, but it&#8217;s never struck me as quite how I want things done.</p><div><hr></div><p>At the moment, the Entity system is mostly done, but without other systems it&#8217;s like a heart with no legs. Or arms. Or pulse.</p><p>&#8230;</p><p>There are a few big things I need to get done before I really stress test it, the first of which we&#8217;ll be seeing next week: The Game Loop.</p><p>See you then! </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non-901">&lt;Previous post</a> | </em><a href="https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-bd8">Next post&gt;</a></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Fun fact: The explanation that follows is real! This is actually how these enemies were planned out and built to work in the game&#8217;s actual codebase. It&#8217;s obviously a little simplified though.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>We&#8217;ll talk more about the &#8216;game loop&#8217; at a late date, but I&#8217;ll give a quick explainer here if you don&#8217;t know what I mean by &#8216;frames&#8217;: Like film, video games run at certain frame rate, often 60 frames per second. For every frame, the game will update the state of the world (e.g. moving objects will move a tiny bit, animations will go to their next frame, etc.) then render the result onto your screen. At a high enough frame rate, this creates the illusion of a smoothly moving world.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>You might be wondering: some components (like, say, a position component) are gonna get shared around <em>a lot</em>, won&#8217;t that get tedious? But hey, there&#8217;s nothing stopping me from making functions that add a bunch of components at once, bundling them together, sort of like a pseudo-parent. I can even have components automatically add other components that they require in order to work, further cutting down on boilerplate code. The key here is that I&#8217;m not <em>forced </em>to keep behaviors in one bundle. If, in certain cases, I want to split them up, or make a different bundle, it&#8217;s trivial to do so.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Choosing the Right Tools]]></title><description><![CDATA[This series can be read out of order, but here are some navigation links for your convenience:]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-901</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non-901</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Fri, 17 May 2024 03:04:55 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/792cf3a5-ad89-4e01-b266-de0ff6aaad31_840x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This series can be read out of order, but here are some navigation links for your convenience:</em></p><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non-c31">Next Post&gt;</a></em></p><div><hr></div><p>So, I know I&#8217;ve made a big deal out of starting from scratch, but nobody actually does that ever for anything<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. In my case, there&#8217;s a whole bundle of tools I&#8217;ll be relying on third-party software for, but to even get started, I&#8217;ll need to decide on <strong>two </strong>things:</p><ul><li><p>A programming language</p></li><li><p>A game development framework</p></li></ul><h2>What is a programming language, anyway?</h2><p><em>You can&#8217;t just ask me that! </em>Dear reader, to truly answer such a question I would be forced to do you a terrible harm! That is, give you a <em>computer science</em> lesson! No, that wouldn&#8217;t do, it simply wouldn&#8217;t. But&#8230; ah, needs must. Here&#8217;s what we&#8217;ll do: I&#8217;ll give you a unique, very special computer science lesson. It will be so special by being incredibly, magnificently brief! I guarantee, you will learn very little. It&#8217;s not ideal, I know, but it will surely be a mercy compared to the alternative. Now then&#8230;</p><div><hr></div><p>What is a programming language? Put simply, it&#8217;s a set of <em>logically consistent </em>rules for writing text<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>, called a program, which can be turned into instructions that your computer can understand.</p><p>You might have heard that, fundamentally, your computer only deals in 1s and 0s, but how can it ever read text then? Well, inside pretty much any computer is a device called a Central Processing Unit. In (grossly oversimplified) essence, a CPU is a little machine that takes in and puts out numbers using electrical signals. Each is built with a big set of little tasks<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> they can do, called instructions, and each instruction has its own little number to represent it. A program is basically a long list of those little numbers that the CPU reads in order. It&#8217;s even possible to write out that list of instructions directly using something called an assembly language, but this incredibly tedious and difficult, even for relatively simple programs.</p><p>With that in mind, programming languages can be grouped broadly into 2 buckets:</p><ul><li><p>Compiled languages: The more straightforward approach. Compiled languages use a program called a <strong>compiler</strong>, which knows the rules of the language, to read the text you wrote and turn it into a list of instructions for your CPU. You can then run the &#8220;compiled&#8221; program on its own, whenever you want.</p></li><li><p>Interpreted languages: Instead of turning your text into little numbers ahead of time, interpreted languages use a program called an <strong>interpreter</strong>,<strong> </strong>which runs alongside your program, and reads the text to figure out what instructions to give the CPU <em>as your program is running</em>. Interpreted languages tend to be more flexible, since, unlike a compiler, an interpreter doesn&#8217;t need to know what to tell the CPU way ahead of time and can figure things out on the fly. Unfortunately though, this means programs written in interpreted languages are significantly slower, since figuring things out on the fly takes time.</p></li></ul><p>Coming back to my own project: my interest in developing my own engine took serious shape about year ago, when I started more closely following the development of Jai (Jonathan Blow&#8217;s new programming language that he&#8217;s been working on since 2014). Its philosophy and design goals were fascinating. It seeks, in essence, to be a replacement for C++, the current standard for any low-level game development.</p><p>Many popular languages nowadays, like Python and Javascript, are interpreted<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>. This makes them easier to use in many ways than compiled languages like C++, but for tasks like serious game development, where performance is extremely important, the slowness of an interpreted language is unacceptable. In addition to that, many interpreted (and even compiled languages), in pursuit of ease-of-use, tend to obfuscate how your program actually interacts with computer hardware (specifically, for the most part, how your program manages memory). They&#8217;re designed to handle that sort of thing automatically in the background while you deal purely with the abstract logic (i.e. they are &#8216;high-level&#8217;, compared to low-level languages which are closer to the hardware). This does make things easier, but usually results in programs and <em>programmers </em>that don&#8217;t understand what is going on under the hood, which is quite bad for performance.</p><p>C++ is one of the only popular languages that still puts control of the hardware in the hands of the programmer. The language it&#8217;s based on, C, is also like this, but C lacks many of the features of C++ that make it feasible to write very large, complex programs. Even then, the way C++ handles that complexity is, in hindsight, less than ideal, and makes programs unduly hard to maintain and reason about.</p><p>Learning about Jai and the philosophy behind it exposed me to the true gravity of these problems, problems which it is aiming to fix as a C++ replacement. It was also very exciting, the language itself seemed incredibly fun to use compared to the much older and more bloated C++<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a>, which made the task of serious game engine development under said philosophy seem a lot more manageable. </p><p>Unfortunately, Jai is still unavailable to the general public, which left me experimenting with alternatives. But before I talk about that ordeal&#8230;</p><h2>The innovative, portable elephant in the room</h2><p>As I write this, my Nintendo Switch devkit sits demandingly and NDA-protectedly on the corner of my desk&#8230;</p><p>The biggest, most glaring condition I set for myself as part of developing this engine was that it needed to be able to <strong>run on consoles</strong> (in addition to Windows, Mac, and Linux devices) without much additional work. This meant finding a game development framework that supported the major console platforms. What is a game development framework? <em>You can- </em>totally ask me that question, it&#8217;s not that complicated. Such a framework is basically a collection of existing code (a &#8216;library&#8217;) that you can include as part of your program that handles common tasks such as drawing to the screen, collecting input, playing audio, etc. It also <em>abstracts </em>that functionality across platforms, which means code written using that framework will work across any platform it supports. For example, say I want to draw a rectangle on the screen:</p><ul><li><p>If I was handling this stuff myself, I would have to write different code that draws a rectangle for every single platform I wanted to support, taking into account all sorts of things that differ like how the CPU is built, the graphics interface, requests to the operating system, etc. Obviously, this is extremely time-consuming and complicated and requires an obscene amount of technical knowledge.</p></li><li><p>Alternatively, I can simply tell a framework &#8220;Draw me a rectangle!&#8221;<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a> and the framework will handle all the complicated stuff for me, changing how it does things under the hood depending on the platform I&#8217;m targeting. Not only does this save me the effort of actually writing the code that draws the damn rectangle, it means I only have to worry about the platform when it really matters. </p></li></ul><p>As you can see, frameworks are an essential tool, and even amongst indie studios which write their own engines it&#8217;s <em>very, very </em>rare to see someone not using one. So, finding the right framework was a must. And while finding one that supported console development was my main goal, I also had a number of other things in mind when looking:</p><ul><li><p>It should be in line with the philosophy outlined in the previous section, i.e. it should be written in a low-level language and able to be used with a low-level language.</p></li><li><p>It should be primarily designed for 2D game development. Often, frameworks created for 3D development (or both 3D <em>and </em>2D development) tend to come with a lot of unnecessary bloat from the perspective of someone like me, who works only in 2D.</p></li><li><p>It should strike a nice balance between having enough features and still being relatively simple and easy to learn.</p></li><li><p>It should be primarily designed for PC and console development, as opposed to mobile development.</p></li><li><p>It should have a track record of quality, commercial indie games being made with it. This is the best proof that a framework is powerful enough to suit the needs of a commercial game developer like me.</p></li></ul><h2>So how did it go?</h2><p>Annoyingly.</p><p>To my surprise and dismay, pretty much the only framework that advertised itself as having full support for all consoles (aside from those used by full-on engines) was <strong><a href="https://monogame.net/">Monogame</a></strong>. At first, this didn&#8217;t seem like the worst thing in the world. It fit most of my criteria and even came with a number of useful tools. And hey, if it was good enough for Terraria and Celeste, surely it would be good enough for me? But Monogame was built on Microsoft&#8217;s old XNA framework, which means it&#8217;s written almost exclusively in and expects you to use <strong>C#</strong>, Microsoft&#8217;s special little language.</p><p>C# is&#8230; in a weird middle ground. It uses Just-In-Time Compilation, which means rather than being compiled ahead of time, a program called a <em>runtime</em>, which is sort of like an interpreter, compiles your program as it&#8217;s running. Sorta. Look, the details aren&#8217;t important; this basically just means that C# is faster than an interpreted language, but more flexible than a compiled language. But, y&#8217;know, you could also rephrase that as it being less flexible than an interpreted language and slower than a compiled language. Especially because C# <em>hates </em>the idea of giving you control of memory&#8230; but I was blissfully unaware of the full extent of this hatred at the time, and so, with a notepad of full of low-level memory tricks I wanted to try out, I decided to jump in and give Monogame a shot.</p><p>Cut to one month later, and I was at my limit. See, C# <em>technically </em>gives you the ability to do low-level memory control, but holy <em>sh&#8212;</em> it&#8217;s like pulling teeth!! Half the language features aren&#8217;t built for it, you have to ask pretty please every time you do it, and whenever I wanted to ask a question about it every forum answer (and half the official documentation&#8230; and ChatGPT&#8230;) basically just told me &#8220;you&#8217;re crazy, don&#8217;t do it&#8221;! They literally call it &#8220;unsafe code&#8221;! And of course, the runtime is so full of implicit background behavior when it comes to this stuff that there&#8217;s <em>never </em>any easy way of knowing if things are working as intended or if you screwed up and wiped out all the performance benefits you were aiming for. The simplest tasks, stretched out into days of infuriating research and fiddling around! What foolery! What nonsense!</p><h2>Uh&#8230; memory&#8230;?</h2><p>Ah. Crap, another computer science lesson. Alright, let&#8217;s get through this quick, I believe in you.</p><p>You might be familiar with the idea that your computer has <em>memory, </em>i.e. places where it stores information (as little numbers) when the CPU isn&#8217;t using it for its little tasks. But did you know that your computer has different <em>kinds </em>of memory? In essence, different memory components on your computer trade-off between how much data they can store, and how quickly the CPU can access them. So a component might be able to store a lot of data, but take a really long time to access; or it might only store a little bit of data, but be really fast to access. Here&#8217;s an overview of the different memory components in a typical PC:</p><ul><li><p>Hard Drive (or &#8216;the disk&#8217;): This is basically &#8220;cold storage&#8221; for your data. Hard drives are <em>huge, </em>with hundreds of Gigabytes or even Terabytes of storage space<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a>, and they store data even if the computer is powered off, but they&#8217;re <em>really </em>slow to read from. When a game is &#8220;loading&#8221;, it&#8217;s usually because it has to move a bunch of data from the hard drive to RAM. Speaking of&#8230;</p></li><li><p>RAM (Random Access Memory): This is basically where all the programs running on your computer store themselves and any data they&#8217;re currently using, after being loaded in from the hard drive. It&#8217;s called that because you can access any part of it just as quickly as any other part, instead of having to wait for a <em>literal spinning disk </em>to move to the right position. RAM is pretty big, usually capable of storing several gigabytes on modern PCs. It&#8217;s also much faster to read from than a hard drive, but not as fast as&#8230;</p></li><li><p>CPU cache: Your CPU comes with a number of little memory components called &#8216;caches&#8217;, each one smaller and faster than the last. The smallest ones are usually on the order of a few kilobytes. Whenever a program tries to send a piece of data from RAM to the CPU, it (and some surrounding data<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-8" href="#footnote-8" target="_self">8</a>) gets copied to a CPU cache. The <em>next </em>time your program tries to read data from RAM, the CPU will check if it&#8217;s in a cache first. If it is, great! It can just read it from there instead (this is called a &#8216;cache hit&#8217;<em>, </em>and if the CPU can&#8217;t find the data in a cache, it&#8217;s a &#8216;cache miss&#8217;). This is useful because getting data from a cache can be <em>hundreds </em>of times faster than getting it from RAM. </p></li><li><p>CPU registers: This is where the CPU puts data it&#8217;s directly working on. They&#8217;re tiny, usually just a few bytes. If your CPU is in the middle of, for example, adding two numbers together, each of them is gonna take up one register.</p></li></ul><p>So, bringing it back to the earlier discussion, how do different programming languages interact with this stuff? Generally, anything that has to do with memory is considered fairly low-level, so many languages do their best to make it so you don&#8217;t have to think about it. In C#&#8217;s case, it employs a common strategy called &#8216;garbage-collection&#8217;:</p><ul><li><p>Say I want to store a bunch of numbers for later use: in a garbage-collected language, I just need to say &#8220;store those numbers somewhere!&#8221;, that&#8217;s it. Another special program that runs alongside mine, called the &#8216;garbage collector&#8217; (GC), will hand me a reference I can use to get my numbers back later, and I can go about my merry way. The GC will automatically handle putting and shifting those numbers around in RAM, and cleaning them up when my program doesn&#8217;t need them anymore (it does this by continuously checking if the reference it gave me is still in use).</p></li><li><p>In a &#8216;manual memory-management&#8217; language like C, I would be in control of memory, but also responsible for it. So for my bunch of numbers, I&#8217;d have to explicitly ask the operating system for somewhere (in RAM) to put them, make sure there&#8217;s enough space for them, keep track of exactly where they are, move them somewhere else if e.g. I suddenly need to add more numbers than I have space for, and free up the memory as soon as I no longer needed it.</p></li></ul><p>Wow, manual memory management sounds like a pain! Why bother? Well, as mentioned, it all comes down to performance. Running a GC introduces overhead, but worse than that is that in a GC&#8217;d language you have <em>no clue</em> how your program is actually laid out in memory. This makes it basically impossible to optimize your program for the cache, since the main way of doing that is to very carefully arrange your data sequentially in RAM, to minimize cache misses. This is a <em>big deal, </em>even simple programs can, in practice, run <em>dozens to hundreds </em>of times faster if properly optimized for the cache. This makes low-level memory control a must-have if you&#8217;re serious about performance. But as much as C#&#8217;s design insists otherwise, it doesn&#8217;t have to be a nightmare&#8230;</p><h2>Alternatives</h2><p>I can actually remember the specific line of code that shattered my faith in C#<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-9" href="#footnote-9" target="_self">9</a>&#8230; it was time to try something else. After all, in many ways, C# was diametrically opposed to Jai. It&#8217;s a decades-old language designed by committee with a huge emphasis on automatic memory management. Frankly, it&#8217;s a bit crazy that I got as far with it as I did. Perfect, guaranteed console support was just not worth having to use it, so I loosened my criteria: if I could get a test app running on my Switch devkit, that would be good enough for now. With my expectations properly humbled, I decided to look closer to &#8220;home&#8221;.</p><p>It was through my research at this point that I realized that we&#8217;re truly living in a bit of a renaissance period for C-like languages. Jon Blow wasn&#8217;t the only person who saw the need for a new C successor; over the past decade or so, several projects have sprung up trying to be more pleasant alternatives to C/C++ for systems programmers. Go, Rust, Zig, V, and many more have been slowly carving out their own niches. But for my purposes, one language stood out to me: ODIN. </p><p>Created by Bill &#8220;gingerBill&#8221; Hall, <strong><a href="https://odin-lang.org/">ODIN</a></strong> is a C-like language whose design philosophy is squarely in the same camp as Jai&#8217;s, and it shows in both the syntax and language features. It was created primarily to help with the creation of the VFX software that Bill works on, and seems to have been a huge success in that regard<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-10" href="#footnote-10" target="_self">10</a>. It&#8217;s also considered to be incredibly well suited for low-level game development, in-part thanks to its out-of-the-box support for many existing C-based game dev libraries. Most notably: Simple Directmedia Layer 2.</p><p><strong><a href="https://www.libsdl.org/">SDL2</a></strong> is an incredibly popular game development framework with an extremely impressive track record. While I was already aware of it from my previous research and some dabbling years prior, I had glossed over it since it didn&#8217;t seem to advertise having out-of-the-box support for consoles. But color me surprised! Upon closer investigation of an old readme document hosted at the back of their website, I uncovered the existence of official Switch and Xbox ports of the framework!<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-11" href="#footnote-11" target="_self">11</a></p><p>(Now is probably a good time to mention: getting one&#8217;s hands on these sorts of things is a bit of a pain. Console manufacturers are extremely secretive about the inner workings of their machines, so ports like these can&#8217;t be hosted freely alongside the normal framework. They can only be made available to <em>licensed </em>developers, on pain of a visit from the ninjas. Fortunately, I have <em>all </em>the right credentials, so getting access to the Switch port of SDL2 was just a matter of a few twitter DMs.)</p><h2>Hope!</h2><p>The temptation was too strong, I had to try it! I set up Odin on my PC and hooked up my new project with the SDL2 port. This was it! The moment of truth! Would I be able to get a test app written in Odin using SDL2 working on my devkit? </p><p>&#8230;</p><p>Yes, of course, or else we wouldn&#8217;t be here. Did you not read the introduction post? </p><p>Still, it was quite the endeavor. I learned a <em>lot </em>about setting up compilers. After a weekend of desperate trial and error I actually leapt out of my chair and cheered once I got my devkit to display an empty red screen. I can&#8217;t talk too much about it unfortunately, or else the ninjas will be at <em>my </em>door, but let&#8217;s just say that Odin working seamlessly with any existing C code was a big deal.</p><h2>Great success!</h2><p>Indeed. I&#8217;ve been working on the engine for the past couple of weeks now and ODIN has been delightful to use and learn about, a huge improvement over C#. I feel like almost every day I&#8217;m picking up new and better ways of doing things. I didn&#8217;t really touch on Object-Oriented vs Data-Oriented programming in this post, but I might cover it later on; my opinions on the matter are seeing quite the rapid evolution thanks to this language.</p><p>My plan right now is to hit some basic milestones in terms of engine features, build a small prototype game using those features, and make sure everything still works on Switch. An Xbox port of SDL2 exists&#8230; I think&#8230; but that will have to wait until I get access to a devkit. As for Playstation, that console will probably be the hardest to support; it uses a platform-specific graphics API, which in simple terms means that a lot of work specific to that platform has to be put in to get a framework working on it. Likely because of this, SDL2 sadly doesn&#8217;t seem to have a Playstation port. So&#8230; Switch now, Xbox later, and Playstation, uh&#8230; maybe never? We&#8217;ll see.</p><p>Next post, we&#8217;ll finally get into actual engine design, so look forward to it! See you then!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non">&lt;&lt;Introduction</a> | <a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non-c31">Next Post&gt;</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Oh? Primitive tech youtubers? Did they build their own cameras too, HMM???</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Yes, plain text! You could open up notepad right now, write in any language you want, and it would work just fine as a program.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Individual instructions really are quite simple. Most of them just boil down to moving a single number from one place to another. It&#8217;s amazing what you can do if you&#8217;re running billions of them per second though.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Game Maker Language is a bit of an interesting case. By default, it&#8217;s interpreted, but it can be compiled for a performance boost. As part of its compilation process, it actually gets converted into C++ as an intermediate step! However, the resulting program is still usually quite inefficient compared to one that was written directly in C++.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>For reference, C++ was released to the public in <em>1985 </em>and <em>to this day </em>still receives a steady stream of revisions and new features. C has been around even longer of course, since 1972, but while it&#8217;s still being maintained it, by design, hasn&#8217;t changed much since then.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p>Well, more like &#8220;<code>SDL_RenderDrawRect(renderer, rect)</code>&#8221;, but you get the idea.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p>As of 2024 lol.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-8" href="#footnote-anchor-8" class="footnote-number" contenteditable="false" target="_self">8</a><div class="footnote-content"><p>As in, whatever data happens to be literally, physically stored next to it in RAM.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-9" href="#footnote-anchor-9" class="footnote-number" contenteditable="false" target="_self">9</a><div class="footnote-content"><p><code>public ref T RefExample&lt;T&gt;(){ return ref new T(); }</code>: Terrible, foolish, ERROR!!</p><p><code>public ref T RefExample&lt;T&gt;(){ return ref (new T[1])[0]; }</code>: Completely fine.</p><p>Clown language.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-10" href="#footnote-anchor-10" class="footnote-number" contenteditable="false" target="_self">10</a><div class="footnote-content"><p>Said software, EmberGen, is written fully in Odin, and now apparently the industry standard for generating fire and smoke VFX. This is in large part thanks to the fact that, unlike it&#8217;s predecessors, it can render in a matter of minutes what used to take hours or <em>days!!!</em> Wow!</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-11" href="#footnote-anchor-11" class="footnote-number" contenteditable="false" target="_self">11</a><div class="footnote-content"><p>Honorable mention: <strong><a href="https://www.raylib.com/">Raylib</a></strong> is a similar framework that was originally envisioned as a friendlier competitor to SDL. Unfortunately, its only switch port is locked away tightly in the avaricious grasp of the publisher who commissioned it, and, based on our conversations, the framework&#8217;s creator does not seem to have the freedom to release it to licensed developers, despite his willingness to do so.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Game Engine Dev, Explained for Non-Programmers: Introduction]]></title><description><![CDATA[8 years ago, I took my first real steps as a game developer by learning Game Maker (for those who don&#8217;t know what kind of software it is, just think of it like Photoshop or Ableton but for games).]]></description><link>https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/game-engine-dev-explained-for-non</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Thu, 09 May 2024 16:34:54 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0f77368b-f67d-459f-8cc8-57a7d4a9f2ab_840x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>8 years ago, I took my first real steps as a game developer by learning Game Maker (for those who don&#8217;t know what kind of software it is, just think of it like Photoshop or Ableton but for games). Certain developers I admired at the time (in fact, most of them) had seen great success with it, and the kind of games I wanted to make didn&#8217;t seem to require anything that anyone else was offering, so I figured it was a safe bet. And it certainly paid off! It had everything one needed built-in; it was approachable enough to learn quickly and yet, almost paradoxically, hands-off enough that, used in the right way, one could learn a great deal of universally applicable game dev knowledge and make some really great stuff. I truly loved it, and, while I dabbled with all sorts of other tools, Game Maker quickly became my specialty. It&#8217;s been with me my entire career, including on all the commercial games I&#8217;ve worked on. And now, after nearly 3 years of tackling <a href="https://store.steampowered.com/app/1887400/ANTONBLAST/">my biggest project ever</a> with it, I can confidently say:</p><p><strong>It</strong><em><strong> chafes!!!</strong></em></p><p>Constant tiny annoyances! Endless compile times! Crappy tools that get utterly crushed by literally any specialized alternative! An IDE so laggy I don&#8217;t even use it anymore<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>! Pathetic performance un-helped by a pitiful profiler! Literally <em>as I am writing this</em> github desktop is chugging away trying to revert a mountain of files that got messed up after an update. </p><p>It&#8217;s still the engine I know and love, but on a project this size every flaw is exacerbated, and every annoyance gives strength to <em>the voice</em>. What voice, you ask? The voice of my pride, as an engineer! The voice telling me that I won&#8217;t be a true sage until I roll my own CPU out of sand and <em>grit. </em>The voice that won&#8217;t be chained to the prescribed toolset of a <em>company, </em>no matter how nice their head of product is<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>! The voice that wants to know <em>what&#8217;s going on in memory! </em>But most importantly, it&#8217;s the voice that <s>belongs to Jon Blow</s> wants me to grow, take on the challenges that yesterday&#8217;s me couldn&#8217;t!</p><p>And so, what is left to do when my tools no longer fit my hands but to roll up my sleeves and make ones that do? I have about half a year until the end of my current project and the start of something new. Will I, in that time, manage to make something production-ready, or will I be forced back into Game Maker&#8217;s abrasive embrace? Only I can decide, that&#8217;s the Handmade spirit! Or something.</p><h2>What are you doing exactly?</h2><p>Let&#8217;s take a step back. Earlier, I drew an analogy between Game Maker and software like Photoshop, but what does that mean? Essentially, &#8220;Game Engines&#8221; like GM and similar software (Unity, Godot, etc.) are collections of tools for making games, like a level editor and asset browser, built around central software framework that makes it easy to write only that code which your specific game needs (by providing certain code abstractions, seamlessly interfacing with aforementioned tools, and handling low-level tasks like drawing to the screen or getting user input). My goal is to replicate that entire stack of tools with a combination of specialized third-party software and code libraries alongside a mountain of my own custom code.</p><h2>Isn&#8217;t that, like, really hard?</h2><p>When I was starting out as a game dev, I was pretty adamant about avoiding engine development. I would hear (horror?) stories of developers spending 6+ years endlessly working on the same project, just building and rebuilding their stack. I was an <em>artist, </em>and I wanted to make <em>games </em>dammit, not waste my youth fiddling with some backend. And this attitude was and still is reinforced by the community at large, on both sides of the aisle: engine development is something engineers do for its own sake, not something worth attempting if your main goal was to produce an artful end product in a timely manner. </p><p>But after 8 years, from my current perspective, a project like this seems extremely doable, even on this timeframe! And for all the reasons listed above and more, there&#8217;s plenty of benefits to doing this beyond just my self-satisfaction as a software engineer<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>. Plus, that&#8217;s not just a crazy inside view opinion, there are a bunch of examples of experienced developers producing great works without the help of a third-party engine (Braid and FTL come to mind, both are games which were made mostly from scratch in 1-2 years).</p><p>So what explains the discrepancy? Well, I&#8217;ve got a lot of things going for me:</p><ul><li><p>I have years of experience pushing an existing engine to its limits, trying to make as comfortable as possible to work with. This has given me a very, very clear vision of what I&#8217;m trying to make for myself.</p></li><li><p>I&#8217;ll be working almost entirely in 2D, which is a <em>huge </em>load off. True 3D development is an entirely different beast which I very much do <em>not </em>have the confidence or interest in tackling.</p></li><li><p>As mentioned, I have a very precise idea of what kind of games I&#8217;ll be making with the engine, but will nonetheless not be making a full commercial game with it from the start. Certainly some test prototypes, but they will be quickly discarded. This will let me focus on the engine itself until it&#8217;s in a fairly stable state without the aimlessness that typically comes with such an approach.</p></li><li><p>I&#8217;ll be using existing libraries and tools like SDL and LDtk as much as possible to handle difficult problems that would take too long to solve myself. I&#8217;ll talk more about what this means exactly in later posts.</p></li><li><p>It&#8217;s 2024. The amount of support and tools available to developers nowadays is enormous compared to even 10 years ago. As a research tool, ChatGPT alone has been a tremendous help in my work on this so far (though not without its fair share of frustrations).</p></li></ul><p>I would still not recommend this approach to anyone starting out in game dev and, in fact, I still think Game Maker is a great tool for beginners, even those setting out to make a commercial product. But for those with enough experience, something like this is certainly not as daunting as some make it out to be, and can be a great way to improve yourself, your working environment, and your ultimate product. At least, I think. Maybe I&#8217;ll change my tune in 6 months.</p><h2>Why blog about this? Who is this for?</h2><p>Engine development is a complicated winding path that will require a lot of careful design decisions. I feel like sorting out my thoughts on things to the point that I can explain them to a layman will be a useful exercise. As the title suggests, this series is intended to be legible for non-programmers, though it should hopefully also be of interest to programmers of any experience level. Basically, <strong>it&#8217;s my hope that anyone interested in this part of the game development process, even just as a consumer, will come away with something valuable after reading this series.</strong> </p><p>Also, I&#8217;ll be figuring things out as I go, and I think that&#8217;s the best state to be in when writing something like this. Often, those who&#8217;ve walked far along the path can find it difficult to relay that knowledge back to those just starting out, as so much of it has been compressed into intuitions and shorthand. As someone who&#8217;s just slightly ahead of those starting out, there&#8217;s no better time to wave back at them and point out where next to walk.</p><p>And who knows, maybe I&#8217;ll get a bit of fame and status out of this. For what other reason does anyone do anything?</p><h2>Where are you at now?</h2><p>As of a few days ago, I managed to compile and run a test app I made, using the ODIN language and SDL, on the Nintendo Switch (it&#8217;s got a moving character and everything, wow!). This gives me the confidence I need to move forward with these tools, and I&#8217;ve got a big ol&#8217; todo list that I&#8217;m ready to start churning through (there&#8217;s a couple dozen list items and each one will probably get its own post&#8230; yay&#8230;). In my next post, I&#8217;ll talk about how I ended up choosing the ODIN language, so look forward to that!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for free to get the next post via email!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><em><a href="https://massimog.substack.com/p/game-engine-dev-explained-for-non-901">Next Post&gt;</a></em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Thanks to GMEdit!</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Sorry Russell</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>I want to emphasize this point somewhat. So often, I hear that it&#8217;s fine to make your own engine&#8230; if you&#8217;re trying to learn, get better as a software engineer. But if this was the only reason I would <em>not </em>be tackling this project. I want to make something that will improve the game development experience for me, <em>that&#8217;s </em>my primary driving force, and in my opinion, it should be yours too.</p></div></div>]]></content:encoded></item><item><title><![CDATA[How Void Stranger rethinks hardcore puzzlers]]></title><description><![CDATA[Back in 2019 I put out a video essay analyzing System Erasure's previous game, ZeroRanger, mostly as a way of seeing if making video essays about games might be fun enough to spend time on. I really enjoyed that game, but I distinctly remember that after beating it for the first time that I felt a bit disappointed in the scarcity of the narrative content relative to its high quality.]]></description><link>https://blog.massimogauthier.com/p/how-void-stranger-rethinks-hardcore</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/how-void-stranger-rethinks-hardcore</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Sun, 17 Sep 2023 19:51:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!lR46!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lR46!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lR46!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!lR46!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!lR46!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!lR46!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lR46!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:266190,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lR46!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!lR46!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!lR46!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!lR46!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00b154eb-3d4f-45a6-a9f6-8e6006b50b99_1920x1080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Well, not <em>just</em> that.</figcaption></figure></div><p>Back in 2019 I put out a video essay analyzing System Erasure's previous game, ZeroRanger, mostly as a way of seeing if making video essays about games might be fun enough to spend time on<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. I <em>really </em>enjoyed that game, but I distinctly remember that after beating it for the first time that I felt a bit disappointed in the scarcity of the narrative content relative to its high quality. SE seems to have acausally taken that unspoken disappointment as a challenge because their excellent new <a href="https://www.puzzles.wiki/wiki/Sokoban">Sokoban</a> puzzler <a href="https://store.steampowered.com/app/2121980/Void_Stranger/">Void Stranger</a> is a bottomless fucking rabbit hole of lore, plot, and secret endings<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>. But rather than spoil people on that for no good reason, I fell into one of my classic game design fugue states and wrote something more in the vein of my old ZeroRanger vid, boiling down what I think are the most innovative aspects of Void Stranger's design. And I will proceed to talk about that after this very important message:</p><p><strong>WARNING: This essay contains MAJOR gameplay spoilers for Void Stranger (as well as very minor spoilers for some other popular puzzle games). The game is best experienced blind. No seriously, the discord has like 5 levels of spoiler channel so people can get help without spoiling themselves. If you haven't played the game go play it (it's 10/10, highly recommend), if you don't want to then watch <a href="https://www.youtube.com/watch?v=HSkOdL2zJM4">the release trailer</a> and reconsider. If you still don't want to, then try playing ZeroRanger and realizing how good SE is at making games that appeal to people outside of that game's genre niche. This will surely convince you and you will not make the foolish mistake of continuing to scroll out of laziness.</strong></p><h1>The traditional formula</h1><p>To understand the interesting thing I want to point out in Void Stranger's design, it's useful to look at how a typical hardcore puzzle game is laid out. The most basic recipe is to have the game split up into isolated puzzles accessible from a bare-bones level select, with sets of harder puzzles being unlocked by beating easier ones. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jXNx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jXNx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!jXNx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!jXNx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!jXNx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jXNx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;LTTP: Snakebird - &#8220;God is dead. God remains dead. And we have killed him.\&quot;  | NeoGAF&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="LTTP: Snakebird - &#8220;God is dead. God remains dead. And we have killed him.&quot;  | NeoGAF" title="LTTP: Snakebird - &#8220;God is dead. God remains dead. And we have killed him.&quot;  | NeoGAF" srcset="https://substackcdn.com/image/fetch/$s_!jXNx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!jXNx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!jXNx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!jXNx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6008cd2-e235-4eb9-80fb-e7f60e692554_1920x1080.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Snakebird level select. This game has a very basic structure, but it&#8217;s pretty good too. I took this screenshot from a forum post claiming that &#8220;God is dead and only Snakebird remains&#8221;.</figcaption></figure></div><p>More interesting games such as Baba is You or The Witness tend to play with the "level select" side of things by including more obscure puzzles outside the context of the normal levels as well as an exploration element (among other things). But one thing remains almost constant across all games in this genre: The puzzle is sacrosanct. You will, with very few exceptions, always be given the exact same tools and starting conditions every time you start up the same puzzle. To expand on this point:</p><ul><li><p>There is no way to earn new abilities (outside of new knowledge) that can be applied to previous puzzles.</p></li><li><p>Puzzles generally have (roughly) one intended solution.</p></li><li><p>You can undo mistakes freely.</p></li><li><p>Because of all this, you aren't really expected to solve the same puzzle more than once.</p></li></ul><p>And this <em>makes sense</em>:</p><ul><li><p>Granting you new abilities that make puzzle solving easier is, in a sense, just removing the actual puzzle and replacing it with an easier one.</p></li><li><p>Admittedly the "one intended solution" is less of a hard rule, but slide too far in the other direction and you've basically just made a strategy game.</p></li><li><p>The challenge of solving a puzzle isn't in the execution, solving a puzzle more than once is just a tedious exercise in memorization. On a similar note, not giving the player the freedom to undo and reset at their leisure usually just introduces unnecessary friction rather than any real challenge.</p></li></ul><p>But Void Stranger's great innovation is that it, amazingly, breaks all of these rules and <em>gets away with it</em> through some very clever design decisions. Let's take a look at its structure and see how it differs.</p><h1>First impressions</h1><p>To recap, here's how the game appears to be structured at first glance:</p><ul><li><p>Your character starts on the floor of a dungeon and must make progress by reaching the stairs located somewhere else on the same floor (the entire floor is always visible and stairs are helpfully highlighted should anything be obscuring them). Reaching the stairs puts them on the next floor down. There is no other way to traverse floors; you cannot travel backwards.</p></li><li><p>Reaching the stairs often necessitates solving a (difficult!) puzzle that involves moving floor tiles around, manipulating predictable enemies, and pushing blocks and statues. Stuff in the level only moves when you do.</p></li><li><p>Many floors contain optional objectives. Obtaining these is usually significantly harder than simply just reaching the stairs. Oftentimes one of the optional objectives is a chest that grants you a locust idol when opened.</p></li><li><p>Falling into an empty floor space or running into an enemy kills you, consuming a locust idol and resetting the puzzle to its initial state. There's no way to undo actions, so it's sometimes necessary to kill yourself if the puzzle has been put into an unsolvable state. Presumably, something will happen if you die without any locust idols (This is just one of the many unexplained mysteries the game presents you with as you climb downwards).</p></li><li><p>Some floors contain NPCs, murals, or memories that will expand on the game's plot and lore, as well as hint at secret content.</p></li><li><p>It's worth noting that virtually no mechanics are outright explained and the player is left to figure out pretty much everything by themselves, though as we&#8217;ll see in a moment important stuff tends to be strongly hinted at.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!36Dr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!36Dr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!36Dr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!36Dr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!36Dr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!36Dr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:563438,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!36Dr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!36Dr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!36Dr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!36Dr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c68d6f6-ec69-4ee5-902d-4f5f806180c7_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">This floor&#8217;s puzzle is like Frogger, but uses snakes. It is not like the Snake one, which uses a big millipede.</figcaption></figure></div></li></ul><p>So the game seems, at the moment, like a curious mix of a dungeon crawler and the more traditional puzzler structure seen above. Certainly a lot of quality of life features are missing in service of the strange life system and forced linear progression, but overall puzzles still function like the sealed off systems we discussed. </p><p>Now, speaking of the life system: if you die without any locusts (which will happen to most players, this game is tough) you'll be given the option to continue right where you left off or give up. The game does not indicate at this point what the downside to this is<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>, so I imagine most players choose to continue on rather than risk losing progress. Continuing on afflicts you with the VOIDED status, allowing you to die freely without losing any new locust idols you pick up. Funnily enough, this status brings the game closer to the traditional style by allowing unlimited resets, but it also serves as the trigger for the first part of the game's interesting structural subversions. </p><p>Before I get to that though I'll mention briefly one of the more unique NPC interactions found about halfway through the dungeon. Your progress is blocked off by a large head, who (incapable of moving) demonstrates a secret way to use the smirking statues you've been seeing throughout your climb: By falling down in front of one, all your locust idols will be consumed and you'll be skipped ahead an equal number of floors. This is your first glimpse at how movement between floors isn&#8217;t as rigid as it might seem<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>.</p><h1>Second loop</h1><p>Once you've managed to traverse the entire dungeon, about 200 floors, one of two things will happen depending on whether you have the VOIDED status or not. If you've been absurdly careful and managed to not void out you're rewarded with the game's first real ending. But the much more likely outcome is that you've voided out and will therefore be presented with a hauntingly beautiful vocal musical number, the game's "bad" ending. You&#8217;re then forced to use the hidden functionality of a four-eyed statue (also found throughout the dungeon), which clears your VOIDED status but resets you back to the top of the dungeon.</p><p>At first, this set up seems pretty rough, and if the game was simply asking you to solve the same couple-hundred puzzles again (but without making mistakes this time) it would be a pretty egregious design flaw. But right before resetting you're given a pretty blatant hint on how to use one of the game's main hidden systems. Roughly 20 floors down is a "brand" room. Arranging the tiles here in the fairly obvious 6x6 pattern being shoved in your face unlocks a hidden room that grants you the "Memory" item, allowing you to speak to rocks (who drop gameplay hints and lore). From this point on, the second half of Void Stranger's gameplay starts to unfold: an ARG exploration element that recontextualizes the entire game. If you've been taking notes on the murals found throughout the dungeon you'll notice that each one is shortly preceded by a brand room with the exact right set up to draw the brand depicted by that mural<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UOMB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UOMB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!UOMB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!UOMB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!UOMB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UOMB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:722568,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UOMB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!UOMB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!UOMB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!UOMB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ab76768-8e03-4927-9a74-437a238cb13b_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The first brand room literally starts out in the correct configuration, you just need to remove the stairs. I&#8217;d say &#8220;don&#8217;t ask how I know&#8221;, but it seems pretty obvious.</figcaption></figure></div><p>As you proceed through your second run, new NPC interactions and information obtainable from rocks scatter bits of novelty throughout the old floors. More interestingly though, you'll pick up two items from the secret brand rooms that make progress much faster: wings to fly over gaps and a sword that can kill enemies (which you get from one of the game's sick-ass <em>Sokoban boss fights</em>). Once-brutal puzzles become trivial, which brilliantly weaves that exhilarating feeling you get from using cheat codes for the first time right into normal gameplay. You tear your way through to the bottom of the dungeon once more. If you've voided again (Sokoban boss fights ain't easy!) you'll be reset, but keep your items. On your third run, thanks to some new interactions on the first few floors, you'll learn how to collect much more locusts, as well as how to use those locusts to discover shortcuts. A knowledgeable player with the right items can go straight to the bottom of the dungeon in minutes, a task that might&#8217;ve originally taken them hours. Thanks to this fascinating knowledge-based pseudo-roguelite<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a> progression, reaching the bottom without voiding just gets easier and easier.</p><h1>Hardmode(s)</h1><p>Once you've obtained the Void Stranger's first good ending, you'll be put through two additional gameplay modes. The first is a return to the game's initial structure, with harder versions of every level, colloquially known as "Hardmode". While you can technically obtain the items again in this run, the game initially nudges you in the direction of not doing this by removing them from their original locations. This is a purer puzzle-solving mode, with some of the hardest puzzles in the game, but it won't matter if you void out. Plus, a determined player <em>can </em>get around solving them if they really feel the need to. </p><p>Once you've beaten Hardmode, you're sent into a third mode that's much more focused on the secret-hunting exploration side of the game. Levels are back to their original, easy configuration, and every item is unlocked from the start, but reaching the ending is no longer a simple matter of getting to the bottom. You'll have to use all your secret tricks to jump around the dungeon and probe around until you find what you need, training you to observe carefully and follow up on the mysteries left unsolved. The gameplay this reminds me the most of is Outer Wilds, but I feel Void Stranger's more rigid structure and ability to mix things up with long strings of hardcore puzzles<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a> lends it more engaging and varied pacing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dXED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dXED!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!dXED!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!dXED!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!dXED!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dXED!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Complete (?) Rumor Map : r/outerwilds&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Complete (?) Rumor Map : r/outerwilds" title="Complete (?) Rumor Map : r/outerwilds" srcset="https://substackcdn.com/image/fetch/$s_!dXED!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!dXED!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!dXED!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!dXED!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F573fde6f-3666-443e-b571-30f1e97e7280_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Unlike Outer Wilds, you don&#8217;t get a convenient chart included in the game like this in Void Stranger, you have to download this separate program called &#8220;Discord&#8221;.</figcaption></figure></div><p>Once you've completed all three main modes you aren't left with any obvious direction to go in. But, like with any great puzzle-exploration game, combining all your skills and investigating those secrets that have been right under your nose from the start eventually brings you to the game's "final" ending... which I see no need to spoil here!</p><p>...</p><p>Ok, I can't help myself, <strong>post-game spoiler warning, decode with <a href="https://rot13.com/">ROT13</a>:</strong> Vg vf *nofbyhgryl vafnar* gung arneyl 100% orngvat guvf *40-ubhe pelcgvp Fbxbona* tnzr haybpxf na *ragver frpbaq tnzr* va n *pbzcyrgryl qvssrerag traer*. Gur snpg gung guvf jnf abg rira uvagrq ng naljurer va nal znexrgvat zngrevny fubjf gehr qribgvba gb gur Neg naq V pbzzraq Flfgrz Renfher sbe vg.</p><h1>Wrap up</h1><p>Now that we've covered both the traditional structure of hardcore puzzlers and how Void Stranger diverges from it, let's review those traditional rules and how the game avoids the problems that might arise from breaking them:</p><ul><li><p><strong>No new abilities and only solving puzzles once:</strong> By getting the player to loop through puzzles multiple times, Void Stranger can maintain the purity of the puzzle solving on the first run while shifting the player's focus to exploration and secret finding on any further run. Because of this shifted focus, the game is free recontextualize the puzzle with new abilities, even if it ruins the difficulty, because the challenge no longer lies in the puzzle itself.</p></li><li><p><strong>Freedom to undo:</strong> On later runs that are trying to avoid voiding, limited resets add a welcome sense of challenge to puzzles that have technically already been solved. But thanks to the safety net that voiding provides, Void Stranger is still able to provide this freedom to undo when it&#8217;s trying to present a purer form of puzzle solving.</p></li><li><p><strong>Open-ended puzzles:</strong> While most puzzles still have roughly one intended solution, optional objectives and the slightly open-ended nature of some puzzles (especially once you have items) blends well with the lives system and adds a unique element of resource management and risk-reward not typically found in the genre. Notably, a certain common optional collectable only appears when you aren&#8217;t voided, preserving that risk element and often providing novel challenge on later runs.</p></li></ul><p>And there you have it. Eero Lahtinen and Antti Ukkola have further proven to be game dev geniuses and I hope this game makes them gobs of money so they can fly to a convention somewhere where I can congratulate them in person for once again Saving Indie Games. Oh, and if you two are reading this and want to get this game onto the switch, DM me on twitter or discord or something.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>It was fun, but not as much as actually making games. I won't link that video here for every day I strive to stop being the number one world champion at embarrassing myself.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>This is probably the only game I've ever played that makes me want to write a lore doc, probably in part due to the tragic lack of attention it's been getting (i.e. no one's done it for me).</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>As much as savvy players might suspect one exists.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>I'll also note that this dumps players straight into some of the game's harder levels with 0 locusts, making it quite likely they'll void out if they haven't yet.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>The brand rooms <em>precede </em>the murals, which, cleverly, makes it impossible for players to draw those brands on their first run without outside knowledge, with the exception of the first brand and your personal brand.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p>To be perfectly clear here, the game features no randomization. Well, with a couple of minor exceptions, but they&#8217;re not really relevant.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p>Even the "exploration" mode ends with a series of unique puzzles that <em>require </em>the items, rather than treating them as cheat codes. Also an awesome bomberman-esque boss fight.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Better definitions for interactive stories]]></title><description><![CDATA[I've noticed over the years a certain category of narrative media that I really enjoy.]]></description><link>https://blog.massimogauthier.com/p/better-definitions-for-interactive</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/better-definitions-for-interactive</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Thu, 02 Mar 2023 19:45:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5XeX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've noticed over the years a certain category of narrative media that I really enjoy. It&#8217;s often lumped in under this nebulous umbrella category of "game narratives", but I find that this category is too unspecific to really describe the kind of works I&#8217;m thinking of. This makes it difficult to talk about, think about, and appreciate them, so this post is gonna be my attempt to carve out a better definition for them. First though, I&#8217;ll have to establish some terms:</p><h2>Crafted vs. Emergent</h2><p>Often when talking about game narrative people like to bring up "emergent narrative". These are stories that were not specifically foreseen by a game's designers, but arise organically through the interaction of the player and the game's rules. Examples include stuff like:</p><ul><li><p>Getting a crazy strong build in a roguelike game but running into a boss that hard counters it and snatches away your victory at the last moment.</p></li><li><p>A chess master making a critical blunder against a longstanding rival.</p></li><li><p>Losing a unit you've grown attached to in a strategy game with permadeath.</p></li><li><p>Playing a violent action game as a pacifist and imagining in-game characters' reactions.</p></li></ul><p>While designers can craft their game to encourage such moments, they're never the kind of thing where the <em>exact </em>situation has been planned out, the way it is in most books and movies. It's very much one of the unique strengths of the medium. And it's really cool! Plenty of my favorite games do very little narratively besides emergent stuff.</p><p>But.</p><p>What I really want to distinguish in people's minds in this essay are <em>crafted </em>narratives whose interactive element is fundamental to them, stories that simply would not function without it. I mean "crafted" in this case as more or less the opposite of emergent, narrative elements that <em>have </em>been meticulously planned out, where the designer does know pretty much exactly what every player is going to be experiencing. Many of you can probably think of a few examples of what I'm talking about but to really drive the point home I've developed a simple chart that should make what I'm talking about clearer...</p><h2>The chart</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5XeX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5XeX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!5XeX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!5XeX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!5XeX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5XeX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png" width="900" height="900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/576e7c80-8e42-45ac-a567-7badce554579_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39030,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5XeX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!5XeX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!5XeX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!5XeX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F576e7c80-8e42-45ac-a567-7badce554579_900x900.png 1456w" sizes="100vw" loading="lazy" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There's two axes to go over here:</p><ul><li><p><strong>Pure Game vs. Pure Narrative</strong>: Examples that are higher up on the chart will be what you would intuitively consider more "game-like" rather than "story-like"; they will have fewer narrative elements and what narrative elements they do have will lean more towards emergent narrative rather than crafted. Conversely, examples at the very bottom will have little-to-no interactivity whatsoever.</p></li><li><p><strong>Non-interactive vs. Interactive story</strong>: If an example is further to the right of the chart, that means its interactivity is more fundamental to telling its story. This is really about asking the question "How much would the story suffer if you stripped out all the interactivity? Would it still make sense or be interesting?". Important to note: this does <em><strong>not </strong></em>refer to a story's linearity or non-linearity. For example: The Beginner's Guide would not make sense without its interactive elements, and as such would be placed on the far right of the chart, yet it still features an extremely linear narrative with a single ending and very few optional branches along the way. On the other hand, many Choose Your Own Adventure novels feature several different endings, but only limited interactivity which does not add much to the story, and would therefore be placed close to the left side of the chart.</p></li></ul><p>Here's the chart divided into rough sections, examples to come:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1kT1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1kT1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!1kT1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!1kT1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!1kT1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1kT1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png" width="900" height="900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:122816,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1kT1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!1kT1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!1kT1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!1kT1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a698b7e-53f1-466a-ad77-8f7bf7ee6fa1_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Note that the chart, categories, and following examples here are not rigid or exhaustive. It's not "everything in this genre goes here and nothing else", it's "the typical work in this genre would probably be placed around here in my opinion". With that disclaimer out of the way, I'll explain my thinking for each category (though this is all really an exercise in pointing out the right side of the chart, so if you just want to see that you can skip to the bottom of this list):</p><ul><li><p><strong>Games With Basically No Story/Games With A Small Amount Of Linear Story That Is Not Strongly Tied To The Gameplay (Top left):</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D1yJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D1yJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!D1yJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!D1yJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!D1yJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D1yJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png" width="366" height="366" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:366,&quot;bytes&quot;:127204,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!D1yJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!D1yJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!D1yJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!D1yJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b10c9b-0524-44f6-81f7-45d2b3f28d00_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Minesweeper, Super Mario 64, and Tohou 6: EoSD</figcaption></figure></div><p>Mostly self explanatory, this is what is traditionally thought of as a pure "game", e.g. chess, minesweeper, most sports, etc. Further down in these areas would be games with a minimal story that mostly serves as a backdrop to the gameplay. Tohou is an interesting example here, since it has such a rich lore that has spawned countless fanworks set in its universe. Take most games in the series in isolation though and you'll get a fairly simple plot told through only brief (yet charming) character interactions. So... top left. All told though I'd argue it's these exact properties, in combination with the developers' permissive attitude towards copyright, that makes the franchise so easy to remix and reinterpret.</p></li><li><p><strong>Strategy Games (Mid Right-Top Right):</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HO_R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HO_R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!HO_R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!HO_R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!HO_R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HO_R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png" width="364" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:364,&quot;bytes&quot;:178765,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HO_R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!HO_R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!HO_R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!HO_R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18e36199-6a4d-48d3-90b1-dd5e32b9311b_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Plague Inc. and Sid Meier&#8217;s Civilization 5</figcaption></figure></div><p>This is another category of games with minimal narrative elements. The distinction here though is that the stories these games tell usually end up completely nonsensical when divorced from the game. On the left side you can still extract a simple yet meaningful narrative (two armies engage in tactical warfare until one claims victory, Mario adventures through many obstacles to defeat bowser and rescue the princess). A mostly complete, somewhat interesting story is told whether or not Magnus Carlsen is leading one of the armies or stomping on koopas. But on the right side, aside from maybe the premise, the stories are pretty nonsensical on their own. Medieval india fails miserably to conquer the world using war elephants? Virus MEGABUTTS wipes out humanity? These "stories" may be kinda funny, or at least a starting point for an actual narrative, but their full impact is only present when you're the one authoring them through the game, or at least watching someone else do so. I called this category strategy games, but I think a lot of management/simulation/sandbox games fit around here as well.</p></li><li><p><strong>???/Ghost Train Rides (Center Left): </strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yJv1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yJv1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!yJv1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!yJv1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!yJv1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yJv1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png" width="362" height="362" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:362,&quot;bytes&quot;:222987,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yJv1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!yJv1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!yJv1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!yJv1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fed1bb-6f8a-4349-b304-455a49e9d71c_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Call of Duty: Modern Warfare 3 (Singleplayer) and The Last Of Us</figcaption></figure></div><p><a href="https://www.youtube.com/watch?v=OCKZsyz7B5U">Ghost Train Ride</a> is a term for linear cinematic adventure games coined by Yahtzee Croshaw. For the purposes of this chart we'll just say these are games with a fairly significant narrative element, but whose interactivity is relatively shallow and ineffectual when it comes to adding things to its story. It&#8217;s unsurprising that a few of these games have seen recent semi-successful attempts at film or TV adaptations. But these games' interactivity still arguably adds *something* to their stories, even if it's just a slightly enhanced sense of immersion. The ??? category is reserved for games whose interactivity, in theory, adds literally nothing to the story. Games like this might exist, but to be honest the idea sounds so difficult to pull off while being so unappealing that I didn't really want to spend time thinking of examples.</p></li><li><p><strong>CRPGs/JRPGs (Upper Middle/Middle):</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xi9c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xi9c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!xi9c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!xi9c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!xi9c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xi9c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png" width="362" height="362" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6314300c-0b93-423b-918c-26337d092e34_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:362,&quot;bytes&quot;:301651,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xi9c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!xi9c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!xi9c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!xi9c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6314300c-0b93-423b-918c-26337d092e34_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">FTL and Xenoblade Chronicles</figcaption></figure></div><p>The middle of the chart is probably one of the looser zones, and the labels' vertical placement come mostly from the fact that JRPGs tend to have more predetermined storylines and characters, while western RPGs put more emphasis on open-endedness and building your own character<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. RPGs end up further right on the chart since pretty much at the genre's core is the idea that the game mechanics are all representative of some kind narrative element. They don't make it all the way to the right though, since a lot of the time their stories can somewhat survive being separated from a gameplay context (just look at cutscene compilations on youtube).</p></li><li><p><strong>Not Videogames (Bottom Left):</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VFoW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VFoW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!VFoW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!VFoW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!VFoW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VFoW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png" width="360" height="360" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/336708db-41a1-48e9-a41e-8f0542885e76_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:360,&quot;bytes&quot;:379446,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VFoW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!VFoW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!VFoW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!VFoW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F336708db-41a1-48e9-a41e-8f0542885e76_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The Hobbit and Sesame Street</figcaption></figure></div><p>This part of the chart is reserved for traditional media with little-to-no interactive elements, such as books, movies, etc. I thought it would be funny to put sesame street a bit further on the right since shows like that technically do sometimes expect the viewer to interact with its prompts.</p></li><li><p><strong>Visual Novels (Bottom Middle):</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z4P8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z4P8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!Z4P8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!Z4P8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!Z4P8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z4P8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png" width="362" height="362" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:362,&quot;bytes&quot;:530710,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Z4P8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!Z4P8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!Z4P8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!Z4P8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a63d759-eb19-4023-9fba-7d31d537f0db_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fate/stay night and Doki Doki Literature Club</figcaption></figure></div><p>Further to the right at the bottom we have visual novels. These are works of fiction that have basically no elements we'd consider game-like aside from dialogue choices and saving and loading, but who will often speak directly to the reader (or a reader stand-in) and incorporate their nature as a choice-driven branching storyline into the text itself. I'd say examples that do this to a greater extent would be further right in this zone, and examples that are simply a more traditional story that branches would be further left.</p></li><li><p><strong>Point-And-Click Adventures:</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aMa8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aMa8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!aMa8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!aMa8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!aMa8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aMa8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png" width="368" height="368" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:368,&quot;bytes&quot;:472726,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aMa8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!aMa8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!aMa8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!aMa8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6556028e-bf28-420e-9ed6-adb83c19afd5_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Broken Age and Ace Attorney</figcaption></figure></div><p>Right above visual novels we have point-and-click adventures, and I think many games even in this category get lumped in with VNs due to their text-heavy narrative focused approach. I think the key differentiator here, and why these games can stretch so far to the right of the chart while still hovering above the very bottom, is that the gameplay elements are very often centered around forcing the player to truly understand the very deliberately crafted plot, setting, and characters. Progress both as a player of the game and as a reader of the story is, at many points, one and the same. With all that said, some may find Ace Attorney's placement here a bit unusual, what with all its non-game direct adaptations. Personally though I've always found these adaptations to be hollow, limited imitations of the source material, which just serves to reinforce my impression that AA's story and those like it were really made from the ground up to be experienced through interaction.</p></li><li><p><strong>(Good) Walking Simulators (Bottom Right):</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!il2t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!il2t!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!il2t!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!il2t!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!il2t!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!il2t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png" width="370" height="370" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b5374a37-8f1f-4b21-a63e-49171b272052_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:370,&quot;bytes&quot;:503381,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!il2t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!il2t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!il2t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!il2t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5374a37-8f1f-4b21-a63e-49171b272052_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The Stanley Parable</figcaption></figure></div><p>i.e. games made by Davey Wreden. When thinking of this corner of the chart it's difficult to point out anything else. These are works that are almost wholly lacking in what one would consider traditional game-like elements and yet are so entirely constructed around their interactivity that removing it would be incoherent. I think it's natural that even the works themselves wrestle in-text with being categorized as a game or not, what that even means. That is in large part the failure of our existing categories that I want try and rectify with this essay, partly in the hopes that it'll help people talk about and create more works like these and those in the next category.</p></li></ul><p>Last up, we have the (center right) area that I really want to talk about, not covered in the bullet list above, that this chart has hopefully helped you to distinguish:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tgk_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tgk_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!tgk_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!tgk_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!tgk_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tgk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png" width="342" height="342" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:342,&quot;bytes&quot;:609731,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tgk_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!tgk_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!tgk_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!tgk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc114e9dc-734a-4559-be1d-0756a99ff94a_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The Witness, Portal, Inscryption, Undertale, and Papers Please</figcaption></figure></div><p>The title "Indie Zone" is a little tongue-in-cheek but it does hold a kernel of truth. I think, because this category of work is so unclear in people's minds, works that try to capitalize on what make it special have a hard time selling themselves and their strengths. They therefore only end up being made by those willing to take a gamble or experiment. No wonder many are lauded as innovative; creative revolutions within the medium.</p><p>Anyway, I think it should be clear what I'm trying to point out at this point: these are works that incorporate many elements of a traditional game, yet most or all of those elements directly serve the telling of a <em>specific </em>story (or the conveyance of some <em>deliberate narrative thing</em>, from grand themes to small jokes). Well, an important general point here is that this isn't a one-way street, the narrative will also take influence from the "pure" game design and what it's best at conveying. This powerful synthesis of crafted narrative and interactivity has produced some of my favorite art, and yet I rarely see it discussed on its own (outside of people analyzing a specific game that does it well).</p><p>As a short aside, I think it's worth distinguishing the upper part of "The Indie Zone" a bit. I think games here are still trying to convey something deliberate, but go about it in a more open-ended or subtle way. I put The Witness here cause it's a great example of this, basically a plot-less open world of meticulously crafted environments all loudly yet oh-so-obtusely screaming the same specific message on the nature of epiphany. </p><div><hr></div><p>Here&#8217;s the full chart with zones + every example:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xRte!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xRte!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!xRte!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!xRte!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!xRte!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xRte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png" width="900" height="900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:510203,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xRte!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 424w, https://substackcdn.com/image/fetch/$s_!xRte!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 848w, https://substackcdn.com/image/fetch/$s_!xRte!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 1272w, https://substackcdn.com/image/fetch/$s_!xRte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf1f7381-b109-4be6-9db9-ff3b490752e2_900x900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>On Meta</h2><p>I&#8217;ve got one last big point I'd like to make. You may have noticed that a lot of the works on the right side of the chart have many narrative elements people describe as "meta". I don't think this is a coincidence, and I could write a whole essay about this (and probably will at some point), but I'll summarize a little here. </p><p>Meta elements often get a bad rap due to often being used as a cheap way to inject comedy, shock value, or "depth" into a story that hasn't been built around them. I think in non-interactive mediums there's only so far you can take meta, and despite the examples you can pull of its artful use it remains merely a useful tool. But interactive mediums are different, and I'd argue that the fact of the reader/player <em>interacting </em>with the story makes it meta <em>by default.</em> Read about the creation of a lot of these narratives and you'll see the creators incorporating meta elements because it's <em>just what makes sense</em>. The player is not a passive observer, and thus in a fleshed out self-consistent story it takes more effort to <em>separate </em>them as a participant than it does to weave their influence and internal experience in as a natural part of the story. Sure, this can be done poorly, but I'd argue that this is more akin to the fact that not every game with jumping and platforms is as good as your favorite Mario game, despite technically sharing the same core gameplay.</p><h2>Conclusion</h2><p>To wrap things up I just want to reiterate, this was meant to serve as a crystallization of my thoughts around a lot of my favorite games. I feel I now have a better model of the kind of art I want to make and see more of, and hopefully you do too! As always when it comes to someone presenting a four squares chart that can supposedly plot every example of some complex multidimensional thing, it's important to mention that this isn't meant to exhaustively describe every aspect of a story, and works can differ significantly along plenty of other dimensions. Plus, this is just a toy model based primarily on my intuitions rather than hard data, it's definitely open to refinement! If you have disagreements on aspects of it or even just on the placement of a specific game I'd love to hear your opinions in the comments or on twitter/discord!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/p/better-definitions-for-interactive?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/p/better-definitions-for-interactive?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>I know this is something of a controversial statement, and one could find plenty of examples to the contrary, but in my mind this has always been the central thing people have been pointing to when using the Western/Japanese RPG distinction.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Creating ANTONBLAST's Layer Jump System]]></title><description><![CDATA[Crossposted from the ANTONBLAST kickstarter page]]></description><link>https://blog.massimogauthier.com/p/creating-antonblasts-layer-jump-system</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/creating-antonblasts-layer-jump-system</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Thu, 02 Jun 2022 17:15:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3PNQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em><a href="https://www.kickstarter.com/projects/summitsphere/antonblast/posts/3522465">Crossposted from the ANTONBLAST kickstarter page</a></em></p><p>Heyo, it&#8217;s me, lovable lead programmer on ANTONBLAST Massimo Gauthier, here to talk about how I implemented the layer jumping mechanic! By following this simple guide, you too can become authentically inspired by 90s and 2000s era 2D platformers and their weird pseudo-3D gimmicks!</p><p>This system went through a few iterations throughout development (before I realized VB Wario Land was doing it better than me and just kinda copied how they did it). I'll just be focusing on how the final implementation works, so careful about making any inferences about how easy something like this is to build from scratch.</p><p>If you&#8217;re having trouble reading the code at any point, I&#8217;d recommend pasting into an empty script in Game Maker just to get the syntax highlighting. Substack seems to unfortunately not be great when it comes to code blocks.</p><h1><strong>Design Constraints</strong></h1><p>Starting out, here are the requirements and constraints for the layer system (laid out during the design stage or while experimenting with earlier iterations):</p><ul><li><p>The game will only ever have 2 layers, a background and a foreground layer</p></li><li><p>A parallax effect is in place. We decided on a horizontal ratio of 0.5, meaning the camera will move about half as much on the background layer for any movement on the foreground layer. I'll expand a bit more on this later.</p></li><li><p>Anton can jump between layers by interacting with springs. Doing so will cause him to jump to a corresponding background/foreground position based on his position and the parallax adjustment.</p></li><li><p>Layer jumps always take the same amount of time.</p></li><li><p>Anton will virtually always land on the ground after layer jumping (there are a few acceptable exceptions, such as if no ground is available to land on).</p></li><li><p>The player has a small amount of horizontal control over a layer jump.</p></li><li><p>Anton will always appear to travel in a small vertical arc during a jump.</p></li></ul><h1><strong>Parallax</strong></h1><p>Probably the most annoying thing to figure out and adjust for with layer jumping and basically everything else having to do with the layer system was the parallax. To summarize briefly, the <em>true</em> position (when it comes to collision and interaction) of things on the background layer does not correspond to their <em>rendered</em> position, as that has to be adjusted in order to create a parallax effect. <a href="https://gamemaker.io/en/blog/coffee-break-tutorials-parallax-scrolling-gml">Here's a Game Maker blog post explaining parallax in more detail.</a> Normally this would not be such a big deal, but when you have stuff moving between layers that becomes an issue, as an object's true position according to the collision system can suddenly not correspond to where it's "supposed" to be according to what's actually being shown to the player. Usually this manifested as the object (Anton) snapping or warping around in strange ways after a layer jump.</p><p>To fix this, you have to take a position on one layer and convert it to a position on the other. Initially we were doing this in all sorts of places but I eventually consolidated things to a single function:</p><pre><code>function parallax_adjust(_position, _backToForeground=false){
    if(_backToForeground){
        var _camPosAtPosition = (_position - VIEW_WIDTH/2)/PARALLAX_RATIO;
        return round(_position + clamp(_camPosAtPosition, 0, room_width - VIEW_WIDTH)*PARALLAX_RATIO);
    }
    else{
        return round(_position - clamp(_position - VIEW_WIDTH/2, 0, room_width - VIEW_WIDTH)*PARALLAX_RATIO);
    }
}</code></pre><p>Let's break things down. This function takes an x position from one layer and converts to the equivalent x position on the other. You can specify with the second parameter whether you are converting from background to foreground or vice-versa. This might seem a little abstract, so I will now employ VISUAL LEARNING to help you conceptualize what is being done here. First, consider this example level layout (foreground elements are red and background elements are orange):</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CpQB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CpQB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 424w, https://substackcdn.com/image/fetch/$s_!CpQB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 848w, https://substackcdn.com/image/fetch/$s_!CpQB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 1272w, https://substackcdn.com/image/fetch/$s_!CpQB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CpQB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png" width="768" height="216" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/74add04f-bc0b-4524-824c-870a6396dec4_768x216.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:216,&quot;width&quot;:768,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1550,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CpQB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 424w, https://substackcdn.com/image/fetch/$s_!CpQB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 848w, https://substackcdn.com/image/fetch/$s_!CpQB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 1272w, https://substackcdn.com/image/fetch/$s_!CpQB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F74add04f-bc0b-4524-824c-870a6396dec4_768x216.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Here's what things look like for the player when Anton (technically, the camera <em>following</em> Anton) is fully on the left side of the stage:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T9N5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T9N5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 424w, https://substackcdn.com/image/fetch/$s_!T9N5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 848w, https://substackcdn.com/image/fetch/$s_!T9N5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 1272w, https://substackcdn.com/image/fetch/$s_!T9N5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T9N5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png" width="384" height="216" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:216,&quot;width&quot;:384,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2136,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T9N5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 424w, https://substackcdn.com/image/fetch/$s_!T9N5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 848w, https://substackcdn.com/image/fetch/$s_!T9N5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 1272w, https://substackcdn.com/image/fetch/$s_!T9N5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F79fa2cd7-7134-495c-aad7-c46e9802bc95_384x216.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>No parallax is applied at this point, so the background's true and rendered positions are the same:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p2j4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p2j4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 424w, https://substackcdn.com/image/fetch/$s_!p2j4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 848w, https://substackcdn.com/image/fetch/$s_!p2j4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 1272w, https://substackcdn.com/image/fetch/$s_!p2j4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p2j4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png" width="768" height="216" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/960748db-dded-436d-9a11-d3200ba166c0_768x216.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:216,&quot;width&quot;:768,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2641,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p2j4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 424w, https://substackcdn.com/image/fetch/$s_!p2j4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 848w, https://substackcdn.com/image/fetch/$s_!p2j4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 1272w, https://substackcdn.com/image/fetch/$s_!p2j4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F960748db-dded-436d-9a11-d3200ba166c0_768x216.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Now here&#8217;s what happens when Anton moves to the center of the stage. The background needs to move only half as slow as the camera. To achieve this effect, the background's rendered position (shown here in transparent grey) is set to be equal to its true position + the camera's current x position multiplied by the parallax ratio (0.5 in our case) (the camera's x position is the <em>leftmost</em> side of the black rectangle):</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HpFb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HpFb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 424w, https://substackcdn.com/image/fetch/$s_!HpFb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 848w, https://substackcdn.com/image/fetch/$s_!HpFb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 1272w, https://substackcdn.com/image/fetch/$s_!HpFb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HpFb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png" width="768" height="216" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/aabfe288-18eb-4737-8917-b158e44ce1df_768x216.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:216,&quot;width&quot;:768,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2828,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HpFb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 424w, https://substackcdn.com/image/fetch/$s_!HpFb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 848w, https://substackcdn.com/image/fetch/$s_!HpFb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 1272w, https://substackcdn.com/image/fetch/$s_!HpFb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faabfe288-18eb-4737-8917-b158e44ce1df_768x216.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Here's what the player sees:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lvFf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lvFf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 424w, https://substackcdn.com/image/fetch/$s_!lvFf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 848w, https://substackcdn.com/image/fetch/$s_!lvFf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 1272w, https://substackcdn.com/image/fetch/$s_!lvFf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lvFf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png" width="384" height="216" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:216,&quot;width&quot;:384,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2020,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lvFf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 424w, https://substackcdn.com/image/fetch/$s_!lvFf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 848w, https://substackcdn.com/image/fetch/$s_!lvFf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 1272w, https://substackcdn.com/image/fetch/$s_!lvFf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F42ffdaf5-3656-4de3-9e2b-abe1aef1a571_384x216.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>So, back to the conversion function. Basically what we're doing is running the rendering offset in reverse to get back the true position of whatever position we point at in the rendered background. Going step by step:</p><ul><li><p>Foreground to background:</p><ul><li><p>Take where the camera's position would be at the provided foreground position (<code>_position</code>). This is just <code>_position</code> minus half the camera's width (saved in the macro <code>VIEW_WIDTH</code>), with some clamping applied since the camera cannot move past the left or right edge of the room.</p></li><li><p>Multiply this camera position by the <code>PARALLAX_RATIO</code> (also a macro in this case). This gives you the background's rendering offset.</p></li><li><p>Subtract this rendering offset from <code>_position</code> to get the true background position.</p></li></ul></li><li><p>Background to foreground:</p><ul><li><p>Pretty much the same steps as above, but a little more complicated since the camera's position here relative to <code>_position</code> is dependent on the parallax effect. To account for this, simply divide the camera position by <code>PARALLAX_RATIO</code> before clamping it.</p></li><li><p>You also need to <em>add</em> the rendering offset to <code>_position</code> here instead of subtracting it to get the desired foreground position.</p></li></ul></li></ul><h1><strong>A Special Trick</strong></h1><p>Now that we've got a way of adjusting for parallax it's finally time to tackle the jump itself. Though you might be asking yourself at this point: "The parallax adjustment can only handle positions two layers! Isn't a jump supposed to smoothly move between them? How are you interpolating between the start and end positions?" This is in fact what I was trying to do at first. But then I discovered VB Wario Land's <em><strong>special trick</strong></em>. See if you can spot it in this gif where the player's collision mask is visible:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3PNQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3PNQ!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 424w, https://substackcdn.com/image/fetch/$s_!3PNQ!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 848w, https://substackcdn.com/image/fetch/$s_!3PNQ!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 1272w, https://substackcdn.com/image/fetch/$s_!3PNQ!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3PNQ!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif" width="1152" height="648" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/bbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:648,&quot;width&quot;:1152,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24122368,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3PNQ!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 424w, https://substackcdn.com/image/fetch/$s_!3PNQ!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 848w, https://substackcdn.com/image/fetch/$s_!3PNQ!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 1272w, https://substackcdn.com/image/fetch/$s_!3PNQ!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbab3b07-12a5-41b6-bf49-2e08000bc312_1152x648.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That's right, no physics interpolation is happening at all! As soon as the player initiates a jump, valid <em>grounded</em> position is identified and they are teleported there right away! The actual jumping Anton is just a rendered "ghost" that slowly approaches the true position. This has a number of useful advantages:</p><ul><li><p>You can determine a valid grounded position before the jump even starts by doing the following:</p><ul><li><p>Identify an initial target position using spring's center position adjusted using the parallax adjustment seen previously, as well as a y position slightly higher than the spring's (both of these "target" values can be overridden by the level designer if they want the spring to send you somewhere that isn't quite the exact position that corresponds to the spring's position).</p></li><li><p>If said target position would put the player inside terrain, move them up until they are just above it.</p></li><li><p>If said target position would put the player in midair, move them down until they are on top of terrain, with an exception if no terrain exists below.</p></li></ul></li><li><p>Since you know the player will start in a valid position, you can give the player limited movement control using the existing speed and collision systems without worrying about them getting stuck in a wall or something. Because the ghost Anton is approaching the true position anyway, it takes minimal additional effort to allow this movement while still making it so both the ghost and true positions sync up at the end of a jump.</p></li><li><p>This allows you to know the relative heights of the player's initial and final position, which is invaluable for pre-calculating variables for a jump arc of fixed duration and height for the ghost Anton to follow.</p></li></ul><h1><strong>The Jump Arc</strong></h1><p>The last big step before getting into the nitty gritty details is how to do the jump arc. As we all know, acceleration and deceleration can be modeled using the following kinematic equations:</p><pre><code>d = distance
t = time
a = acceleration (or gravity)
f = final velocity
v = initial velocity

f = v + a*t
d = ((f + v)/2)*t
d = v*t + 0.5*a*t^2
f^2 = v^2 + 2*a*d</code></pre><p>Each half of the jump arc can be modeled by one of these equations. Here's what we know:</p><ul><li><p>The final velocity of the first half and initial velocity of the second half, they're both 0 (i.e. the apex of the jump).</p></li><li><p>The highest point of the jump, which you can use to get the distance value for each half. You can determine the highest point by taking the higher of the starting or final y positions and subtracting a fixed offset (which can be adjusted by the designer). You then subtract the highest point from the starting and final y positions to get the distance values for each half of the jump.</p></li><li><p>The total time of the jump, which remains fixed by design.</p></li></ul><p>Here's what we need to determine:</p><ul><li><p>The time to the apex of the jump, which can then be used to get...</p></li><li><p>The gravity, which remains constant across both halves of the jump.</p></li><li><p>The initial velocity of the first half of the jump.</p></li></ul><p>Since I wasn't able to figure out how to obtain those three variables mathematically, I wrote a bit of code to brute force the problem. This doesn't really affect much performance-wise but if anyone knows how to solve this properly do let me know. Here is the code in question (the <code>y</code> variable here refers to the target y position, which was determined earlier):</p><pre><code>var _layerJumpHighPoint = min(_startY, y) - layerJumpHeight;
var _fallDistance = y - _layerJumpHighPoint;
var _jumpDistance = _startY - _layerJumpHighPoint;
layerJumpGrav = 0;
var _currentJumpTime = 999;
while(_currentJumpTime &gt; layerJumpTime){ 
    layerJumpGrav += 0.01;
    var _timeToApex = sqrt(2*_jumpDistance/layerJumpGrav);
    layerJumpVerticalSpeed = -layerJumpGrav*_timeToApex;
    _currentJumpTime = _timeToApex + sqrt(2*_fallDistance/layerJumpGrav);
}</code></pre><h1><strong>The Details</strong></h1><p>Now that all the big conceptual stuff is out of the way, all that's left to go over is the specific implementation details. Please note that some edits have been made to code snippets keep things focused on the essential functionality, you guys don't need to know the filename for every sound Anton is making while jumping. Also to note, the player object uses a finite state machine, I won't go in-depth on that pattern so <a href="https://gameprogrammingpatterns.com/state.html">here's a good explanation of how it works</a> for those unfamiliar.</p><p>Let's start with the variables used on the player object:</p><pre><code>layerJumpHeight = 25;
layerJumpX = -1;
layerJumpY = -1;
layerJumpVerticalSpeed = 0;
layerJumpGrav = 0;
layerJumpTime = 50;
layerJumpTimer = -1;
layerJumpMaxHorizontalSpeed = 1;</code></pre><ul><li><p>"Height" is the offset used to determine the peak of the jump during the arc calculation. We measure units of distance in pixels, native screen size is 384x216.</p></li><li><p>"X" and "Y" here are used to keep track of the Anton "ghost"'s render position during the jump.</p></li><li><p>As we saw earlier, "Grav" and "VerticalSpeed" are determined during the jump arc calculation and affect the shape of the jump arc.</p></li><li><p>"Time" determines how long a jump takes in frames (at 60 FPS the value given above would be about 0.83 seconds)</p></li><li><p>"Timer" keeps track of how long the player has been in a layer jump. While the end of the jump is not triggered by the timer, it is still useful for other things we'll see later.</p></li><li><p>"MaxHorizontalSpeed" determines how fast the player can move from side to side during a layer jump.</p></li></ul><p>The player triggers a layer jump by standing on a spring in the normal state and jumping (we also check that they aren't holding left or right):</p><pre><code>if (jump){
    var _spring = instance_place(x, y+1, mySpring);
    if(!moveLeft &amp;&amp; !moveRight &amp;&amp; _spring != noone){
        layerJump(_spring.targetX, _spring.targetY);
    }
}</code></pre><pre><code>layerJump = function(_targetX, _targetY){
    myLayer = (myLayer == 0) ? 1 : 0;
    currentState = playerStates.playerLayerJump;
&#9;
    horizontalSpeed = 0;
    verticalSpeed = 0;
&#9;
    if(myLayer == 0) layerJumpX = round(x + camera_get_view_x(PLAYERVIEW)*PARALLAX_RATIO);
    else layerJumpX = round(x - camera_get_view_x(PLAYERVIEW)*PARALLAX_RATIO);
&#9;
    var _startY = y;
    x = _targetX;
    while(
        place_meeting(x, _targetY, myBlock) || 
        place_meeting(x, _targetY, myPassthrough)
    ){
        _targetY -= 1;
        if(_targetY &lt; 0){
&#9;    _targetY = y;
&#9;    break;
&#9;}
    }
    y = _targetY;

    while(
        !(check_below(_targetY) || 
        check_below_passthrough(_targetY))
    ){
        _targetY += 1;
&#9;if(_targetY &gt; room_height){
&#9;    _targetY = y;
&#9;    break;
&#9;}
    }
    y = _targetY;
&#9;

    var _layerJumpHighPoint = min(_startY, y) - layerJumpHeight;
    var _fallDistance = y - _layerJumpHighPoint;
    var _jumpDistance = _startY - _layerJumpHighPoint;
    layerJumpGrav = 0;
    var _currentJumpTime = 999;
    while(_currentJumpTime &gt; layerJumpTime){ 
        layerJumpGrav += 0.01;
        var _timeToApex = sqrt(2*_jumpDistance/layerJumpGrav);
&#9;layerJumpVerticalSpeed = -layerJumpGrav*_timeToApex;
&#9;_currentJumpTime = _timeToApex + sqrt(2*_fallDistance/layerJumpGrav);
    }
&#9;
    layerJumpY = _startY;
    layerJumpTimer = 0;
}</code></pre><p>The layer jump function does a bunch of setup:</p><ul><li><p>Changes the player's layer</p></li><li><p>Puts the player in the layer jump state</p></li><li><p>Zeroes out the player's speed</p></li><li><p>Sets the Anton ghost's initial render position based on the player's current pre-jump position</p></li><li><p>Determines a valid position based on the desired target coordinates and teleports the player there</p></li><li><p>Resets the jump timer</p></li><li><p>Calculates the jump arc, as seen earlier</p></li></ul><p>Once the player object is in the layer jump state, it'll start running this code in its step event:</p><pre><code>layerJumpX = approach(layerJumpX, x, 0.4);
layerJumpX += horizontalSpeed;

var _moveDir = moveRight - moveLeft;
horizontalSpeed = layerJumpMaxHorizontalSpeed*_moveDir;

verticalSpeed = 0;

layerJumpVerticalSpeed += layerJumpGrav;
if(layerJumpVerticalSpeed &lt; 0) layerJumpY += layerJumpVerticalSpeed;
else{
    layerJumpY = approach(layerJumpY, y, layerJumpVerticalSpeed);
    if(layerJumpY == y){
&#9;if(!onGround) verticalSpeed = layerJumpVerticalSpeed;
&#9;    currentState = playerStates.playerNormal;
    }
}
layerJumpTimer++;
</code></pre><p>Said code does the following:</p><ul><li><p>Approach the render position to the player's true position, such that they're guaranteed to sync up before the jump ends.</p></li><li><p>Move the render position based on the player's movement speed.</p></li><li><p>Lets the player move left and right based on the layer jump horizontal speed. This should be done after the previous step to allow collision, which would usually run after this, to properly affect the speed being applied to the render position.</p></li><li><p>Keeps vertical speed at 0, eliminating the normal physics' gravity. Leaving gravity on would cause various issues given this implementation, and it coming into play at all is enough of an edge case that I decided it would be easiest to just disable it.</p></li><li><p>Does the usual physics stuff with gravity and vertical speed to the ghost.</p></li><li><p>Once the ghost is on its way down, checks whether it's reached the target y position. Once it has it transitions the player back to their normal state.</p></li></ul><p>Last but not least, let's take a look at the code that's rendering all this:</p><pre><code>if(currentState == playerStates.playerLayerJump){
    var _scale = map(layerJumpTimer, 0, layerJumpTime, (myLayer == 1) ? 0.5 : 1, spriteScale);
    _drawX = round(layerJumpX + ((myLayer == 0) ? 0 : (camera_get_view_x(PLAYERVIEW)*PARALLAX_RATIO)));
    var _palette = map(_scale,0.5,1,2,1);
    pal_swap_set(playerPalette,_palette,false);
    draw_sprite_ext(
&#9;sprite_index, image_index,
&#9;_drawX, round(layerJumpY),
&#9;_scale, _scale, image_angle, image_blend, image_alpha
    );&#9;
}
</code></pre><p>Going line-by-line:</p><ul><li><p>The player's scale is smoothly adjusted using the layer jump timer as they transition to or from the background.</p></li><li><p>The draw position is determined by taking the render position and adjusting for parallax based on whether the player is in the background or not (remember, the new layer value is set as soon as a jump is initiated).</p></li><li><p>The player's palette is also subtly adjusted (based on their scale) as they change layers to create an atmospheric scattering lighting effect.</p></li><li><p>The player's sprite is then drawn using the values we have painstakingly calculated.</p></li></ul><h1><strong>Extra Tidbits on Keeping the Player in the Background</strong></h1><p>To avoid this post getting too long, I'll be brief with this stuff, especially since it shouldn't be too hard for others to figure out the details. For the most part the player works the same while they're in the background, with a few notable exceptions:</p><ul><li><p>Like other objects that appear both in the foreground and background, Anton is rendered at half size while in the background, and his collision mask is also adjusted to match.</p></li><li><p>The player object's physics variables are halved to keep the movement correct despite the player's small size</p></li><li><p>The player object can only collide with things on the same layer as it. Our current implementation of this is pending a complete rewrite. My intent is to replace the collision functions with ones that have the layer check built-in, perhaps using collision matrices similar to unity.</p></li></ul><h1><strong>Conclusion</strong></h1><p>Well there you have it, all the information you need to totally rip us off as badly as we ripped off, uh, a bunch of stuff actually. There's a surprising amount of platformers that have a gimmick like this. Guess that speaks to its strength as a level design tool! If you have any questions feel free to leave a comment or join our discord and ask in the <code>#your-computer</code> channel, which I hang around in regularly. Thanks for reading, see you next devlog! Oh, and be sure to check out the <a href="https://www.kickstarter.com/projects/summitsphere/antonblast">kickstarter page</a>!</p>]]></content:encoded></item><item><title><![CDATA[The first Lower World Story public demo is out now!]]></title><description><![CDATA[Discussing the past and future of (blogging about) my tactical RPG project]]></description><link>https://blog.massimogauthier.com/p/the-first-lower-world-story-public</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/the-first-lower-world-story-public</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Sat, 24 Jul 2021 22:24:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WlVN!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc58c93dc-201b-41d2-b463-6960be30646a_208x208.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>That&#8217;s right, the first public demo for the game I&#8217;ve been working on since last year is <a href="https://massimog.itch.io/lws-combat-demo">out now on itch</a>! If you haven&#8217;t seen it before I highly recommend at least checking out the store page before reading the rest of this post.</p><div><hr></div><p>There&#8217;s a video on that page where (after a <em>masterfully </em>edited action-packed trailer) I talk a bit about the past and future of this project&#8217;s development. That video marks the end of my plans to vlog the game&#8217;s development (mostly, I might still make the occasional announcement video here and there), and I figured, what better way to break in this blog than to pedantically expand on whatever I was saying there!</p><p>When I started making those videos my thinking was something along the lines of &#8220;Video Games are cool but you can&#8217;t really regularly put out a really great, polished product the same way you can with other kinds of internet content (unless you only make <em>tiny </em>prototypes), which makes it difficult for an unknown developer with very little artistic skill to garner attention online. But if I make regular devlogs I can develop the game I want to <em>and </em>produce regular content for my (future) adoring fans, not to mention leverage the youtube algorithm. And look at <a href="https://www.youtube.com/channel/UCaoqVlqPTH78_xjTjTOMcmQ">all</a> <a href="https://www.youtube.com/channel/UCUmLRMERmJrmUtgnbFfknAg">these </a><a href="https://www.youtube.com/channel/UCrv269YwJzuZL3dH5PCgxUw">other </a><a href="https://www.youtube.com/c/uheartbeast/videos">developers </a>on youtube making it work, this is totally doable! Most likely I won&#8217;t reach <em>quite </em>that level of success, but I&#8217;m sure it&#8217;s worth trying out.&#8221; Now, after a year of actually making devlogs, my thinking hasn&#8217;t actually changed <em>too</em> much, it&#8217;s just (not unexpectedly) developed quite a few caveats that I&#8217;ll be getting into. </p><p>First though I want to say I <em>definitely </em>don&#8217;t regret making those videos, that last part about it being worth it turned out to be entirely true. I&#8217;m now way more comfortable in <a href="https://www.blackmagicdesign.com/products/davinciresolve/">professional editing software</a>, I know what my strengths as a video producer are, I know I can produce a decent video if I ever need to (as well as about how long it&#8217;ll take), and, most importantly, it helped me land my <a href="https://www.massimogauthier.com/antonball-deluxe-2021">first job</a> as a game programmer by being something in my portfolio that really put my sense of humor and adaptability on display (which, turns out, is pretty important in tight-knit indie development environments).</p><p>So then, why switch over to text based blogging? Well&#8230; ever notice notice how popular youtube game devs (except for <a href="https://www.youtube.com/channel/UC_p_9arduPuxM8DHTGIuSOg">Jonas Tyroller</a>) tend to either produce very small games or seem to get stuck working on the same project forever? Turns out making <em>good </em>youtube videos is just as hard as making any other kind of art (shocker), and doing it <em>regularly </em>is almost a full time job. As fun as those videos were to make, putting out even just one of decent quality per month would have left me with upsettingly little time to actually develop my game. Maybe if I was <em>really into </em>making youtube videos as an end, the same way I&#8217;m into making games, I would have been faster and happier working on them, but a lot of the time I just felt like they were slowing me down when it came to the stuff I actually cared about. So here we are. I figure I can still achieve most of the marketing value (and dopaminergic gratification) that I wanted out of those videos for a fraction of the effort just by focusing more on substack and twitter, and people on substack will probably be more interested in the kind of game I&#8217;m making than the average youtube viewer anyway. Plus, this way I can explore a whole bunch of other ideas that I <em>really </em>didn&#8217;t care enough about to turn into full videos. If you&#8217;re interested, you can-</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://massimog.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share Massimog's massiblog&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://massimog.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Massimog's massiblog</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/p/the-first-lower-world-story-public/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/p/the-first-lower-world-story-public/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://twitter.com/massim0g&quot;,&quot;text&quot;:&quot;Follow me on twitter&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://twitter.com/massim0g"><span>Follow me on twitter</span></a></p><p>&#8230;yeah that.</p>]]></content:encoded></item><item><title><![CDATA[Videogames and things that are unrelated to Videogames]]></title><description><![CDATA[Welcome to Massimog's massiblog by me, lovable game developer Massimo Gauthier!]]></description><link>https://blog.massimogauthier.com/p/coming-soon</link><guid isPermaLink="false">https://blog.massimogauthier.com/p/coming-soon</guid><dc:creator><![CDATA[Massimo Gauthier]]></dc:creator><pubDate>Sun, 18 Jul 2021 21:40:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WlVN!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc58c93dc-201b-41d2-b463-6960be30646a_208x208.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to Massimog's massiblog by me, lovable game developer Massimo Gauthier! </p><p>I&#8217;ll start writing posts soon, in the meantime you can subscribe and check out the about page to learn more about me!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.massimogauthier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.massimogauthier.com/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item></channel></rss>