<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Lucid Visions - Blog</title>
    <link rel="self" type="application/atom+xml" href="https://lucidvisions.net/blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-02-15T13:40:00+00:00</updated>
    <id>https://lucidvisions.net/blog/atom.xml</id>
    <entry xml:lang="en">
        <title>2026-02-15</title>
        <published>2026-02-15T13:40:00+00:00</published>
        <updated>2026-02-15T13:40:00+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-046/"/>
        <id>https://lucidvisions.net/blog/2026-046/</id>
        
        <summary type="html">&lt;p&gt;This post is more like an entry in my journal that I&#x27;ve made public, a post
about finding my way in the world of game development.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>2026-02-09 2</title>
        <published>2026-02-09T12:47:22+00:00</published>
        <updated>2026-02-09T12:47:22+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-040-2/"/>
        <id>https://lucidvisions.net/blog/2026-040-2/</id>
        
        <summary type="html">&lt;p&gt;Here is the curated commit log from this last week&#x27;s development.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2026-02-03
&lt;&#x2F;span&gt;&lt;span&gt;feat: removed entity action timer; moves are now turn-blocking anims.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2026-02-03
&lt;&#x2F;span&gt;&lt;span&gt;feat: render entity index in graphical turn queue debug visualization
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2026-02-03
&lt;&#x2F;span&gt;&lt;span&gt;feat: allow mult. entities in turn queue to move on same frame
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2026-02-03
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce reaction to entity death after attack animations play
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2026-02-06
&lt;&#x2F;span&gt;&lt;span&gt;feat: add component to support entity pos interp. during movement
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;This also updates the camera to move with the player as position is
&lt;&#x2F;span&gt;&lt;span&gt;interpolated, having the effect of a smooth-scrolling camera instead of
&lt;&#x2F;span&gt;&lt;span&gt;a camera that moves in units of whole tiles.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>2026-02-09</title>
        <published>2026-02-09T08:40:16+00:00</published>
        <updated>2026-02-09T08:40:16+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-040/"/>
        <id>https://lucidvisions.net/blog/2026-040/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2026-040/">&lt;p&gt;This morning I was thinking about the
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cassandra_(metaphor)&quot;&gt;Cassandra Complex&lt;&#x2F;a&gt; and
general mentality. Over the last few years, recent popular podcasts (which I&#x27;ll
just not name) have repopularized the term. Often the speakers use the term as
cathartic relief, to express some kind of frustration they have over a thought
that they feel mocked, attacked, or persecuted for instead of rewarded for. For
a long time, I thought, &quot;yeah, that sucks...,&quot; but I realized even this is an
acceptance of self-pity. Just reframe the situation, and you have a huge win on
your hands. You want to be right and early. That is where you make outsized
returns. So if you know things should be done differently, do so, even if it
imposes a cost--not an immediate reward--in the short term, because when the
long term pans out and the world catches up, you&#x27;ll be many steps ahead and
ready to launch that book, deploy that technology, release that game, mentor
those people, etc. Even this is a grow-the-pie winning opportunity.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2026-02-02</title>
        <published>2026-02-02T15:12:52+00:00</published>
        <updated>2026-02-02T15:12:52+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-033/"/>
        <id>https://lucidvisions.net/blog/2026-033/</id>
        
        <summary type="html">&lt;p&gt;I missed the update two week&#x27;s ago, and last week&#x27;s update is late, so this post
is a double feature! Recently I am spending a lot more time editing Twitch VODS
to create a YouTube dev log of sorts. This amounts to me listening to me build
my own game instead of spending time building the game, and these blog posts are
also me talking about working instead of &quot;working,&quot; so I will probably start to
make these updates more concise.&lt;&#x2F;p&gt;
&lt;p&gt;I will still post small videos of gameplay directly in some of these updates
which will serve as quick visual checkpoints in the journey, though. I will
also still write the occassional long post when I have a topic I want to discuss
in more detail.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>2026-01-19</title>
        <published>2026-01-19T09:14:17+00:00</published>
        <updated>2026-01-19T09:14:17+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-019/"/>
        <id>https://lucidvisions.net/blog/2026-019/</id>
        
        <summary type="html">&lt;p&gt;This last week I focused on implementing basic animations.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>2026-01-09</title>
        <published>2026-01-09T14:48:17+00:00</published>
        <updated>2026-01-09T14:48:17+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-009/"/>
        <id>https://lucidvisions.net/blog/2026-009/</id>
        
        <summary type="html">&lt;p&gt;This week I imlpemented an entity-component-system, a basic player attack,
entity deletion, and text and textbox rendering.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>2026-01-03</title>
        <published>2026-01-03T13:12:43+00:00</published>
        <updated>2026-01-03T13:12:43+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2026-003/"/>
        <id>https://lucidvisions.net/blog/2026-003/</id>
        
        <summary type="html">&lt;p&gt;This week I implemented A* and added support for multiple enemies to the game.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-12-29</title>
        <published>2025-12-29T15:14:06+00:00</published>
        <updated>2025-12-29T15:14:06+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-363/"/>
        <id>https://lucidvisions.net/blog/2025-363/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-363/">&lt;p&gt;Here is the commit log from last week&#x27;s work.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-12-23
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce default allocator replacement for context
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-26
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce &amp;quot;standard library&amp;quot; (sl) with ring buffer and test
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-26
&lt;&#x2F;span&gt;&lt;span&gt;feat: add heap data structure and min-heap procedures to sl
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-27
&lt;&#x2F;span&gt;&lt;span&gt;feat: separate min_heap out of heap impl; add max_heap; add tests
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main focus this week was pathfinding, but I was also up to some other
peripheral things as well.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, we have the introduction of a default allocator replacement for the Jai
&lt;code&gt;context&lt;&#x2F;code&gt;. Jai passes around a special variable implicitly to each scope called
the context. I think I discussed this earlier, but in short, the context has
various useful properties on it such as the currently preferred logging function
and the preferred default and temporary allocators. In my case, I want to avoid
allocating using the default allocator. I also want to avoid allocating using
the temp allocator as much as possible, but I&#x27;m less concerned about this
because I clear its storage at the beginning of each frame. To catch usage of
the default allocator, I implemented my own allocator which just
&lt;code&gt;assert(false, &quot;[...]&quot;);&lt;&#x2F;code&gt;s on any operation requested of it. I caught a few
issues with this, mostly &lt;code&gt;String.join&lt;&#x2F;code&gt; calls, that I fixed using&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;jai&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-jai &quot;&gt;&lt;code class=&quot;language-jai&quot; data-lang=&quot;jai&quot;&gt;&lt;span&gt;String.join(...,, allocator=temp);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note the &lt;code&gt;,,&lt;&#x2F;code&gt; which is an operator in Jai which allows you to modify &lt;code&gt;context&lt;&#x2F;code&gt;
properties at callsites for arbitrary functions. I would like to use my own
temporary allocators provided through the most basic of arenas, but for Jai
&quot;standard library&quot; things like this, it&#x27;s easier to use Jai&#x27;s temporary
allocator. I could always implement the allocator interface for my arena and
thus use my own backing memory pool, though. Probably a project for a later
time.&lt;&#x2F;p&gt;
&lt;p&gt;Toward pathfinding, I have a simple grid map with uniform cost to move between
squares. I recalled the standard A* algorithm for pathfinding in such
situations. It&#x27;s been over 10 years since I&#x27;ve dealt with A*, though, so I
needed a refresher. I came across
&lt;a href=&quot;https:&#x2F;&#x2F;www.redblobgames.com&#x2F;&quot;&gt;Red Blob Games&lt;&#x2F;a&gt;. This blog and the author&#x27;s few
others linked from it are a wealth of information on algorithms for games,
particularly top-down, 2D ones such as mine. I highly recommend checking the
site out if you&#x27;re interested in such.&lt;&#x2F;p&gt;
&lt;p&gt;I thought I was going to use a queue, and a ring buffer would work well here,
so I implemented one. I followed a great tip from
&lt;a href=&quot;https:&#x2F;&#x2F;www.snellman.net&#x2F;blog&#x2F;archive&#x2F;2016-12-13-ring-buffers&#x2F;&quot;&gt;Juho Snellman&lt;&#x2F;a&gt;
on using only positive indicies (no forced wrap-around via &lt;code&gt;%&lt;&#x2F;code&gt;) and masking the
indicies only when you need to look into the backing storage. Clever and neat.&lt;&#x2F;p&gt;
&lt;p&gt;Now, it turns out I actually wanted a priority queue, so for that I decided to
use a min heap. To my knowledge, Jai does not provide a min heap, so I
implemented one of those as well. To brush up on that, I referenced the classic
&lt;em&gt;Introduction to Data Structures and Alrogithms&lt;&#x2F;em&gt; book. I had learned from this
in college as well. After re-reading a very small portion of this recently, I
admit I cannot recommend it. It does many things well, but I find it overreaches
with its advice in places and also (probably accidentally) misrepresents some
concepts by overfitting descriptions on very narrow use cases in specific
languages, for example by calling the heap a region of garbage-collected
storage. Anyway, it did the job.&lt;&#x2F;p&gt;
&lt;p&gt;I ultimately implemented a heap (heap.jai) and then implemented the min-heap and
max-heap interfaces in respective files that both import heap.jai. I
reintroduced my &quot;sl&quot; concept from when I was playing with data structure
generation via the C preprocessor two or three weeks ago. I have a special file
in the sl module called test.jai which provides a &lt;code&gt;main&lt;&#x2F;code&gt; function and imports
all of the files in sl and tests their features. I have a build.jai file in sl
that builds a binary from test.jai. Building and running the tests is a manual
process, but I don&#x27;t anticipate changing sl so frequently that I&#x27;ll hurt for
any automation, except that one time when I will probably change it, introduce
a bug, and forget to run the tests haha.&lt;&#x2F;p&gt;
&lt;p&gt;I then got to implementing A*. The work is on a branch currently and will be
the subject of this week&#x27;s update.&lt;&#x2F;p&gt;
&lt;p&gt;I also started streaming livecoding (and some gaming) on Twitch, as noted in a
&lt;a href=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-357&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;. The idea here is to maybe build even the
smallest of audiences or at least presence in parallel with building the game
instead of serializing the process. Presumably an audience takes just as long
if not longer to build than the game itself, but what do I know--I&#x27;m totally
new to this aspect!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-12-23</title>
        <published>2025-12-23T20:18:15+00:00</published>
        <updated>2025-12-23T20:18:15+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-357/"/>
        <id>https://lucidvisions.net/blog/2025-357/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-357/">&lt;p&gt;I decided to give streaming a go. You can check out the content
&lt;a href=&quot;https:&#x2F;&#x2F;www.twitch.tv&#x2F;lucidvisionsnet&quot;&gt;here&lt;&#x2F;a&gt;. I will try streaming both
development and games here and there. The motivation is building an audience
more organically in the event I do end up trying to market a game.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-12-22</title>
        <published>2025-12-22T12:42:08+00:00</published>
        <updated>2025-12-22T12:42:08+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-356/"/>
        <id>https://lucidvisions.net/blog/2025-356/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-356/">&lt;p&gt;Here is the gist of the commit log for last week&#x27;s dev log, this post coming in
a bit late.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-12-15
&lt;&#x2F;span&gt;&lt;span&gt;feat: have aseprite exporter export to build&#x2F;, not data&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-15
&lt;&#x2F;span&gt;&lt;span&gt;chore: bump SDL3 to 3.2.28, SDL_image to 3.2.4
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-15
&lt;&#x2F;span&gt;&lt;span&gt;chore: move aseprite exporter into subfolder; add tools&#x2F;jai-sdl3 submod
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-15
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce main.jai; use SDL3 bindings to open window, cap fps
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-19
&lt;&#x2F;span&gt;&lt;span&gt;feat: finish rewriting in Jai; add generated SDL3_image bindings
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-19
&lt;&#x2F;span&gt;&lt;span&gt;feat: parse spawn layer from map asset; randomly spawn player&#x2F;enemy
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-19
&lt;&#x2F;span&gt;&lt;span&gt;feat: implement Map.assign_entity_locations, consolidate asset paths
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The big thing from this last week was revisiting Jai&#x27;s bindings generator and
committing to Jai, not Odin. You can see on the 15th that I was again tempted by
Jai, brought in (my own fork of) the &lt;code&gt;jai-sdl3&lt;&#x2F;code&gt; bindings generator wrapper for
SDL3, and proceeded to rewrite the game code so far in Jai.&lt;&#x2F;p&gt;
&lt;p&gt;After resting two weekends ago, I realized that I didn&#x27;t give the bindings
generator a fair shake. I mentioned it had some issues, or maybe that I was just
holding it wrong. I was just holding it wrong. I wrote a simple C library,
compiled it to dynamic and static libraries, and learned how to build bindings
for these. The knowledge gained here scaled up very quickly to allow me to
understand the bindings generator applied to SDL. I tweaked the implementation
(of just the Linux-targeted portion) of the wrapper to not require a system
installation of SDL. This way I can build bindings for my copies of SDL3 that I
build from source. I also added support for SDL3_image. This was much simpler
than the SDL3 support since SDL_image.h is a comparitively small file. The
generator accepts a visitor that can inspect each declaration as it is processed
and decide what to do--generate bindings with no customization, customize the
generation, or skip the declaration. Since SDL_image.h requires SDL.h, the
bindings generator will try to (re)produce bindings for SDL3. To avoid this,
we just have the visitor ignore declarations that don&#x27;t start with &lt;code&gt;IMG_&lt;&#x2F;code&gt;.
Additionally, we add a header (or footer) to the output to&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;#import &amp;quot;sdl3&amp;quot;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;where &lt;code&gt;sdl3&lt;&#x2F;code&gt; is the name of the module that provides the SDL3 bindings.&lt;&#x2F;p&gt;
&lt;p&gt;The most time-consuming part of the port was rethinking the memory allocators.
In the end, I kept my own arena implementation and still pass those around, but
I also had to figure out what I was going to do with Jai&#x27;s implicit &lt;code&gt;context&lt;&#x2F;code&gt;
passed to each procedure, namely the context&#x27;s allocators. There is a good
thread in the Jai beta discord about this. It reminds us that the main purpose
of the allocators in the context is to help control which memory is used by
modules that you didn&#x27;t implement yourself. So it makes sense for me to keep
passing my own arenas around. I can also call any Jai procedure that uses
temporary storage at will since I clear Jai&#x27;s temporary storage at the
beginning of each frame in my game. In fact, writing this just made me realize
I need to clear my temp arena at the beginning of each frame too, forgot to
do that...&lt;&#x2F;p&gt;
&lt;p&gt;I haven&#x27;t studied the implementation of Jai&#x27;s temporary allocator, but from a
cursor glance I think it allocates its backing storage on-demand and grows the
storage as it needs to. This means that unlike my arenas, the memory is not
allocated entirely up front, and doing so was the entire purpose of introducing
arenas. That can be solved, though, and I believe Jai itself provides some
allocator implementations (&lt;code&gt;Pool&lt;&#x2F;code&gt;?) that allow for up-front, fixed allocation.&lt;&#x2F;p&gt;
&lt;p&gt;Another good idea from the Discord thread was creating a replacement for the
primary allocator assigned in the context that simply calls &lt;code&gt;assert(false)&lt;&#x2F;code&gt; for
any operation requested of it. This way we can detect when we are allocating
from somewhere that we don&#x27;t intend to, assuming code we call respects the
allocators set in the context. I have yet to implement this idea but will do so.&lt;&#x2F;p&gt;
&lt;p&gt;About why Jai instead of Odin or some other language, I am just attracted to the
syntax and power yet simplicity, at least for the features I care most about.
The language feels familiar, not like something I&#x27;ve been writing in for a long
time (I really haven&#x27;t been) but like something that works mostly how I expect
it to.
Like earlier Python, and like good-enough C, it fits in my head. As you can see
from the commit history, I did the entire Jai port in 2, maybe 3 commits. I
could have done it in many, adding this functionality and testing it and
committing, that functionality and testing it and committing, etc., but I just
plowed through it. Amazingly, after getting the program to compile, with the
exception of one case where I passed by value instead of by pointer, everything
just worked.&lt;&#x2F;p&gt;
&lt;p&gt;With the port to Jai done, I&#x27;m getting back to adding features and experimenting
again. To close the week out, I added random entity spawning to the game. I
chose to place spawn points on my test map by creating another tilemap layer in
my Aseprite test map that marks tiles available for spawning. The spawn point
placement in my live game was off, and then I realized that each layer in
Aseprite is exported with a total size determined by a bounding box placed
around the layer&#x27;s top-left-most tile and bottom-right-most tile. I briefly
investigated ways to force the layer to have the same dimensions as all other
layers but didn&#x27;t turn anything up, at least not from the capabilities available
through the Lua API. So I resorted to having 3 tiles IDs in the spawn layer.
The first is the no-tile ID, which is just 0. This is the default in Aseprite,
so I&#x27;m more accommodating it than designing it. The second is the ID that
indicates a tile is available for spawning. The third are placeholder tiles I
use to make sure the spawn layer matches other layers&#x27; dimensions. The
placeholder tile is black (drawn at 50% opacity) in the image below. The yellow
tiles are spawn tiles, the red tiles are collision tiles (on a separate
collision layer), and the light green is moveable space. Beneath the red tiles
are darker green tiles which represent walls or bushes as visible in previous
demo videos.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-356&#x2F;.&#x2F;2025-12-22-test-map.png&quot; alt=&quot;test map in Aseprite&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I use Jai&#x27;s temporary allocator to build an array of spawn tile locations
when I parse the map data, and then I iterate through the entities to randomly
assign them a spawn location. I will probably re-hard-code spawn locations
soon to create reproducible environments for testing other features, but then
I will reenable random spawn in some fashion. The idea plays into the game
concept I have in mind.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-12-14</title>
        <published>2025-12-14T12:35:15+00:00</published>
        <updated>2025-12-14T12:35:15+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-348/"/>
        <id>https://lucidvisions.net/blog/2025-348/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-348/">&lt;p&gt;Here is the commit log from this last week of work.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-12-08
&lt;&#x2F;span&gt;&lt;span&gt;feat: add array and slice-generation macro and test for it
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What happened?! The time has come to switch off of &quot;C++&quot;. Even my beloved
dialect of C++-that-is-actually-mostly-C leaves me with lots of work to do to
create a language I&#x27;m happy to use, before I can get back to the problem at
hand of developing a game.&lt;&#x2F;p&gt;
&lt;p&gt;As I refreshed my memory on search algorithms and studied pathfinding algorithms
I was not familiar with, I realized I&#x27;d benefit from some more sophisticated
data structures. As you can see from the commit above, I started to implement
such data structures. I had three options. The first was to just use containers
from C++&#x27;s standard library. I really didn&#x27;t want to go this route for reasons I
won&#x27;t elaborate on here; suffice to say that I don&#x27;t plan on depending &lt;em&gt;more&lt;&#x2F;em&gt; on
C++ features in general. The second was implmenting my own code generator, like
spec&#x27;ing out code with variables that a (hand-written?) templating tool would
replace with values like type information, running this as a pre-build step, and
then including the generated code in compilation. Again, more work, essentially
somewhat re-implementing C++&#x27;s template features and also portions of what the
third option was: preprocessor macros.&lt;&#x2F;p&gt;
&lt;p&gt;I opted for the third approach since, in theory, it is
write-once-test-a-bunch-use-without-too-much-care-afterward. The natural place
for this work was in the small standard library I was growing. I did prove out
that I could generate a satisfying array type and a corresponding slice type.
But honestly the thought of repeating that for a queue, a priority queue, a
dynamic array, a hash map, etc. dissuaded me from continuing. Not only would I
need to take a lot of care to avoid bugs and write a bunch of tests, I&#x27;d also
need to get into the weeds about which implementation of which data structure
was most optimal (or at least good enough), etc. And I just can&#x27;t be bothered
right now.&lt;&#x2F;p&gt;
&lt;p&gt;Coincidentally, I came across a podcast interviewing Jon Blow
(&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=4Ej_3NKA3pk&quot;&gt;this one&lt;&#x2F;a&gt;? I don&#x27;t quite remember
unfortuantely), asking about why
he was creating Jai and what hope he thought he had of creating a relevant C++
competitor or even replacement. I have been spending a lot of time thinking
about whether to go all-in on Jai, weighing the tradeoffs of an actually-sane
language but one developed and maintained by only a very small number of people
(albeit with a big &lt;a href=&quot;https:&#x2F;&#x2F;www.orderofthesinkingstar.com&#x2F;en&#x2F;&quot;&gt;flagship project&lt;&#x2F;a&gt;
proving that the language is ready for production projects) and an
industry-standard language backed by tools with massive user bases and a much
larger number of contributors. Jon&#x27;s answer was very convincing. In short, he
said that we as a species have not used the same programming language forever.
Even Big Industry switches languages over time. So if we accept that we are not
going to use some particular language forever, then we don&#x27;t have worry about
asking ourselves whether we can compete with things like C++. Instead, we have
to ask ourselves, when we do have a new language that we gravitate toward, which
one will it be? Jai is Jon&#x27;s language in this regard.&lt;&#x2F;p&gt;
&lt;p&gt;The last time I used Jai seriously was one year ago, last winter. Back then I
wrote essentially what I have now in &quot;C++&quot; and SDL (load tilemaps and sprites,
paint a level and a character, move the character around in a 2D world) but in
Jai and its built-in windowing abstraction called &quot;Simp.&quot; So I spent a couple
days brushing up on the language and learning about what had changed since when
I last used it. After refreshing, I gave a Big Think over whether or not to use
Simp again for my project. I decided against it and chose to stay with SDL. So
I started exploring Jai&#x27;s bindings generator tool that is often used to generate
Jai bindings to useful C and C++ utilities people want in their Jai projects.
At the time of this writing, there is an
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;overlord-systems&#x2F;jai-sdl3&quot;&gt;open-source&lt;&#x2F;a&gt; repo that uses the
bindings generator to create SDL3 bindings for Windows, macOS, and Linux. I
forked it and modified it a little to support my own workflow. Instead of the
current open-source behavior which wants a system-installed SDL3 library at
least for the Linux use case, I compile SDL3 from scratch for my project and so
want to point the bindings generator at that library at generation time. I got
it working in the end but suspect I was holding the tool wrong because there
was some non-obvious behavior such as transforming things like
&lt;code&gt;..&#x2F;..&#x2F;build&#x2F;libSDL3.so&lt;&#x2F;code&gt; to &lt;code&gt;__&#x2F;__&#x2F;build&#x2F;libSDL3.so&lt;&#x2F;code&gt; which broke library
loading at runtime untils I made some manual tweaks to the generator and also
stuffed a library in my source code instead of just in the &lt;code&gt;build&#x2F;&lt;&#x2F;code&gt; folder. So
despite it working, I wondered if there was an easier way overall.&lt;&#x2F;p&gt;
&lt;p&gt;Again coincidentally I came across some videos on YouTube about using Odin for
game development. I&#x27;ve known of Odin for years but have never gotten into it.
Odin does provide SDL3 bindings out of the box. Although not shown in the commit
log at the beginning of this post, I&#x27;ve started down the Odin path, first to see
if I enjoy the language enough to keep using it and second to see if it&#x27;s any
easier (by my taste) to achieve what I&#x27;m trying to do with Odin than it has been
with Jai.&lt;&#x2F;p&gt;
&lt;p&gt;Currently I am opening a window via SDL3, painting the window the same gray
background color I was using in my previous C-like implementation, and reacting
to window-close events. The magic to work with my compiled SDL libraries is
simply:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;build SDL3 and emit libraries in &lt;code&gt;.&#x2F;build&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;build my Odin project as follows&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt; odin build src&#x2F; \
&lt;&#x2F;span&gt;&lt;span&gt;   -extra-linker-flags:&amp;quot;-L.&#x2F;build&amp;quot; \
&lt;&#x2F;span&gt;&lt;span&gt;   -out:&amp;quot;.&#x2F;build&#x2F;game&amp;quot; \
&lt;&#x2F;span&gt;&lt;span&gt;   -show-timings
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Now, I am working on porting all my other code from &quot;C++&quot; to Odin. It&#x27;s
annoying, but it&#x27;s best to pay this language-and-tooling cost up front in the
project and have a consistent set of tools I enjoy using for the life of the
project, which will likely be years.&lt;&#x2F;p&gt;
&lt;p&gt;So far, I think Jai is simpler than Odin, both in syntax and in its concepts.
There are very many similarities in the language behavior and syntax. I cannot
tell who inspired whom, presumably Jon--&amp;gt;Bill? Regardless, Odin hasn&#x27;t been
hard to pick up. The thing I spend the most time on is figuring out which types
I should replace C-style buffers and arrays with in Odin. I&#x27;m only a couple days
in, so maybe this is obvious and simple, but I&#x27;m just not there yet, have been
playing more with SDL3 and doing a lot of reading about Odin to get a sense for
its capabilities and staying power.&lt;&#x2F;p&gt;
&lt;p&gt;I did find one thing amusing, that there will be an upcoming change, part of
which is explicitly passing an allocator into some functions
(&lt;a href=&quot;https:&#x2F;&#x2F;odin-lang.org&#x2F;news&#x2F;moving-towards-a-new-core-os&#x2F;&quot;&gt;ref&lt;&#x2F;a&gt;). I am already
doing this everywhere in C, so no big deal. Jai passes a context to every Jai
call that provides things such as an allocator so that callers can easily swap
out the allocator used by third-party code, unless the library author explicitly
chooses to ignore the requested allocator. I think Odin has something similar.&lt;&#x2F;p&gt;
&lt;p&gt;Next week I hope to have at least my previous game implementation but this time
in Odin, if I don&#x27;t decide to switch off of it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-12-07</title>
        <published>2025-12-07T12:53:37+00:00</published>
        <updated>2025-12-07T12:53:37+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-341/"/>
        <id>https://lucidvisions.net/blog/2025-341/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-341/">&lt;p&gt;Now that I&#x27;m doing my own thing, I&#x27;ve got more time on my hands to explore
interesting posts, videos, books, etc. I&#x27;ve also resolved to put more of my
thoughts here in this blog instead of on a variety of disparate networks.&lt;&#x2F;p&gt;
&lt;p&gt;A few days ago I came across a fellow by the name of Sylvan Franklin. I
identify a lot with what his recent journey appears to be, but that story is
for another time.&lt;&#x2F;p&gt;
&lt;p&gt;This post is prompted by
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=aeZEkiaRFZc&quot;&gt;The Addiction that hides in plain slight&lt;&#x2F;a&gt;.
In this post, I&#x27;ll casually mix the target I am writing to.&lt;&#x2F;p&gt;
&lt;p&gt;One point Sylvan brings up is that many of us may have an addiction to
information. I certainly do. I noticed this a few months ago, when I realized
I would hesitate to start house chores or cooking until I found something
interesting to listen to on my Bluetooth headphones.&lt;&#x2F;p&gt;
&lt;p&gt;One day, on a podcast I don&#x27;t remember, I heard someone ask the rhetorical
question, &quot;how much information is enough?&quot; The context implied the full
question was, &quot;how much information is enough to equip you to do what you think
you need to do?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I opened that question up more and realized the next implication, that I didn&#x27;t
actually have an agenda with any of that information. I didn&#x27;t want to do
anything with what I was &quot;learning.&quot; In fact I told myself I was learning, but
in reality I just wanted to be entertained. Learning is evidenced by a change in
behavior, but I wasn&#x27;t changing much. The gotcha was that every now and then I
&lt;em&gt;would&lt;&#x2F;em&gt; consume something that resonated deeply with me--made me feel
understood--or did change the way I thought and lived. What I became addicted to
was the next revelation. But there is no promise of when or where this will
come, much like there is no promise of when you may see something actually
transformative while doomscrolling, be it a social media feed or a collection of
proverbs.&lt;&#x2F;p&gt;
&lt;p&gt;In the end, I was just saturating my brain, hardly giving anything enough time
and thought to personalize it.&lt;&#x2F;p&gt;
&lt;p&gt;You can tell when you&#x27;ve personalized something when you can communicate it to
others without referencing anyone or anything else, that is, by appealing to
personal experience and not authority, without saying, &quot;the Senior Architect
said...,&quot; or, &quot;The Meditations say...&quot; Now, let us not plaigarize, and if you
want to cite the source, that&#x27;s fine, but you haven&#x27;t personalized something if
you &lt;em&gt;appeal&lt;&#x2F;em&gt; to the source. One of the things I appreciate in Sylvan&#x27;s recent
videos I&#x27;ve seen is his self-awareness about his journey being personal.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;what I have to do is, do the hard work of coding everyday, or working on the
math everyday, or reading philosophers everyday and thinking about them&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;You already know the path forward! But perhaps you are not convinced yet. I&#x27;ll
elaborate below, but in the end, only you can convince yourself.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What got you out of information consumption? I&#x27;m looking for the One Trick
(tm).&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you only look externally, you will be searching unsatisfied forever. As you
say, it&#x27;s like climbing a mountain. Looking externally has its benefits, but
ultimately you will see others&#x27; techniques that worked for them. The catch is
that the mountain you climb is your mountain. You can see their mountains from
your mountain, or sometimes at least imagine their mountains. Your terrain might
look like their terrain at times, but ultimately it is not their terrain.&lt;&#x2F;p&gt;
&lt;p&gt;The trick is to know who you are and where you are going. Knowing this in itself
is a journey, and unless you concern yourself with cosmology, is perhaps &lt;em&gt;the&lt;&#x2F;em&gt;
journey.&lt;&#x2F;p&gt;
&lt;p&gt;Who you are means understanding yourself--your nature, your past, your desires,
etc. Consuming information externally is useful for bootstrapping your
search. For example, Zen--as you&#x27;ve also discovered--and the &quot;noting&quot; meditation
technique worked well for me, and understanding this and that about behavioral
psychology was also useful. The trigger to look inward was looking outward for
answers to questions about myself until I realized I am the only one who knows
me. That, and after having asked {God|the Universe|whatever}, &quot;what is my
purpose,&quot; for so long, only to realize I am actually &lt;em&gt;being asked&lt;&#x2F;em&gt; that question
and the asker is just waiting for my response. Coming to know who you are is
often a long journey, perhaps never-ending, depending on your definition of
finished. However, answers to who I am have brought me peace from mind, the
mind being all I was trying to appease by some new toy theory to juggle,
programming language to learn, or other distraction to concentrate on.&lt;&#x2F;p&gt;
&lt;p&gt;Where you are going, as a question, is, &quot;what is your current goal?&quot; If you have
this agitation that your goals are distractions or are perhaps meaningless, also
ask yourself, &quot;why,&quot; regarding what your current goal is. Iterate on that until
you&#x27;ve found something meaningful and satisfying to you and then go with it.
Where you are going will naturally change over time, sometimes multiple times a
year, sometimes once every 5 or 10 years, unless you are the rare
type who pursues a lifelong mission that outlives you. But even then, you will
likely have milestones along the way.&lt;&#x2F;p&gt;
&lt;p&gt;When we know who we are and where we are going, we can return to the deluge of
information, but this time with a natural filter. We&#x27;re here with a direction
and a purpose. We take what is useful, we&#x27;re thankful for all else but are not
distracted by it or {time|compute}-enslaved to it. It&#x27;s worth noting, it&#x27;s
useful to allow ourselves time to play and wander periodically (the search
part of search-exploit, if you will). It&#x27;s not about always filtering but rather
about having the filter and the awareness to apply it as necessary.&lt;&#x2F;p&gt;
&lt;p&gt;The fact that you are already looking means that it is only a matter of time,
so travel with a peaceful mind, fellow sailor!&lt;&#x2F;p&gt;
&lt;p&gt;Given what I understand about you so far, if you have not already read
&lt;em&gt;The Beginning of Infinity&lt;&#x2F;em&gt; (David Deutsch), please do so if you&#x27;re looking
for any bootstrapping.&lt;&#x2F;p&gt;
&lt;p&gt;Aside:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;You program long enough and the patterns emerge, and you start to see things.&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I hadn&#x27;t heard of the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Design_Patterns&quot;&gt;Gang of 4&lt;&#x2F;a&gt;
book until years into my software engineering career. When I read it, everything
in the book I actually cared about was already familiar. I had developed it
myself via the &lt;a href=&quot;https:&#x2F;&#x2F;wiki.c2.com&#x2F;?FeynmanAlgorithm&quot;&gt;Feynman Algorithm&lt;&#x2F;a&gt;. And
I don&#x27;t say this boastfully--I&#x27;m no genius. I just had problems and thought hard
about how to solve them well until I solved them. One amusing thing I noticed,
was that because I had derived the designs myself in-context, it stood out to me
immediately when a peer appealed to the Gang of 4 in a, &quot;well, I don&#x27;t know,
but these guys say...&quot; way. Which is not The Way (tm).&lt;&#x2F;p&gt;
&lt;p&gt;Godspeed.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-12-06</title>
        <published>2025-12-06T09:28:16+00:00</published>
        <updated>2025-12-06T09:28:16+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-340/"/>
        <id>https://lucidvisions.net/blog/2025-340/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-340/">&lt;p&gt;Here&#x27;s the curated commit log for last week.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-12-03
&lt;&#x2F;span&gt;&lt;span&gt;fix: allow player to change facing direction even if they can&amp;#39;t move
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-03
&lt;&#x2F;span&gt;&lt;span&gt;refactor: wrap platform-side details (renderer, screen dims) in struct
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-03
&lt;&#x2F;span&gt;&lt;span&gt;feat: use renderer logical presentation to support arbitrary upscaling
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;This also removes explicit transformation from world tiles to world
&lt;&#x2F;span&gt;&lt;span&gt;pixels as world pixels were only ever used as an intermediate frame when
&lt;&#x2F;span&gt;&lt;span&gt;transforming to screen pixels.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-04
&lt;&#x2F;span&gt;&lt;span&gt;docs: add concept groundwork for first game; separate next game content
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-04
&lt;&#x2F;span&gt;&lt;span&gt;feat: add first enemy, random movement; add entity-entity collision
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-12-05
&lt;&#x2F;span&gt;&lt;span&gt;fix: round lowest visible tile idx toward 0 to fix gap in map_render
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I split my time three ways this week, one by necessity and another mostly by
chance. As this post is about solo game dev, I will share details peripheral
to development from time to time as they are part of the journey. In this case,
the by-necessity thing was applying for health insurance as it is mandatory to
have if living in Japan. I&#x27;ll split that into another short post, though.&lt;&#x2F;p&gt;
&lt;p&gt;The second thing was game design. While I will go into detail about the
mechanics and perhaps level design, I will probably never go into much detail
about the story itself, as I&#x27;d like to leave that up to players to experience
and interpret. I guess I can elaborate a bit on my trajectory until recently,
though. I plan on this being my first game I&#x27;ll publish and sell. I&#x27;d love for
it to be &quot;successful,&quot; but I don&#x27;t bank on that at all, pun intended. What I
want to do is get experience going end-to-end, from an empty file in a text
editor to a game in the market. That will be quite a journey, but that will be
the first iteration. From there I&#x27;ll learn whatever I can and repeat. Toward
that, I don&#x27;t want to develop slop, will try to come up with something creative
and worthwhile for me and others.&lt;&#x2F;p&gt;
&lt;p&gt;At first I had an idea to clone an existing, small game I enjoyed and add some
extensions. This gave me well-scoped goals and removed a big task, which was
novel game design. This was all toward getting some iteration out in public
quickly. Lots of the big indie game hits took 5+ years to develop. I&#x27;d like to
get something out in 1 or 2 years, again, without the aspiration that my title
would mean anything to anyone but me. To date, I&#x27;ve been developing my own game
for something to the tune of 7 weeks or so, and already, my own game design
ideas are popping up in my mind. I did rack my brain for the first two weeks for
some interesting, novel idea I could build a game around, but nothing really
surfaced. I recalled a talk I watched from Jon Blow, I think it was his
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=sIKZyvYj6Tc&quot;&gt;keynote&lt;&#x2F;a&gt; at GDC 2014, where he was
discussing where to find inspiration for interesting games. I&#x27;ve also learned
from my beloved Kojima Hideo-san and also
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@sora_sakurai_jp&quot;&gt;Sakurai Masahiro-san&lt;&#x2F;a&gt;. The thing I
always walked away with, though, is that I&#x27;m not going to sit and &lt;em&gt;try&lt;&#x2F;em&gt; to come
up with an idea that makes for an interesting story or game. And indeed, this
has proven true in my life. Perhaps ironically, it is by &lt;em&gt;not trying&lt;&#x2F;em&gt; that I
have had my most promising ideas for games. This time around has been no
different. As I develop and think about which feature to try and implement next,
ideas seem to naturally bubble up into my mind. &quot;Ah, what if instead of the
game working in this way, it works in that way?&quot; And &quot;that&quot; way turns out to
have some parallel with a transformative experience I had in life, and suddenly
a game feature becomes a metaphor for a realization I had or a way of thinking
that changed my life. And then a game feature and a story element naturally
weave themselves together. I&#x27;m being vague here still so as to not spoil
anything about the story I&#x27;m thinking of writing, but I think I&#x27;ve described
the gist.&lt;&#x2F;p&gt;
&lt;p&gt;Paraphrasing the Tao Te Ching, by not doing, it is done.&lt;&#x2F;p&gt;
&lt;p&gt;I also read and listened to talks about creating game design documents. Creating
one before doing any other work probably works well for the niche of people that
are borderline clairvoyants, but they don&#x27;t work like that for me. In fact, they
smell like the stereotypical project manager who tries to outline a product on
paper and get it right the first time. Of course, the game design document is a
living document in either case, so it does evolve over time,
typically to be come less prescriptive and more descriptive. For me, the way it
works is that I develop something, seem to naturally get interesting ideas over
time related to my life experiences, and then I just jot the ideas down in a
Markdown file checked in at the root of my game&#x27;s repo. It&#x27;s less a design
document and more a journal of the concepts at play in the game and ideas I&#x27;ve
had that I want to explore further. Once I have such ideas, these are seeds from
which I &lt;em&gt;can&lt;&#x2F;em&gt; sit and intentionally think more deeply about and further develop.
Going from 0 to 1 idea-wise, I have no process for. I often just need to let my
mind wander (&#x2F;wonder). Once I have 1, I can play with that intentionally and
grow it in this and that way.&lt;&#x2F;p&gt;
&lt;p&gt;One guiding principle of my game design is that I want to respect the player&#x27;s
time. What I mean by this is try to share an interesting mechanic, idea, or
thought with the player in 3-5 hours. The game would need to be priced
accordingly, but otherwise, I don&#x27;t like the thought of me creating something
that, if a hit, consumes 80+ hours of tens of thousands if not millions of
people&#x27;s time just distracting them from something else in life.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, the third thing was typical feature development. The most interesting
topics this week are rendering logical presentation and adding the first test
enemy.&lt;&#x2F;p&gt;
&lt;p&gt;Regarding logical presentation, after creating a 16x16 px sprite and then
rendering it in my game, I thought the sprite was a bit too small on my monitor.
I had two choices. The first was redraw the sprite, and eventually every asset,
at a larger resolution. The second was to upscale the sprite. The first would
cause the art to take more time, and the second seemed more practical, so I
tried to do the second. When I did that, I got some strange effects. There
seemed to be some sort of visual tearing between tiles. What was in fact
happening was that SDL was applying blending upon upscaling. This is controlled
per-texture using
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_SetTextureScaleMode&quot;&gt;&lt;code&gt;SDL_SetTextureScaleMode&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
SDL3 conveniently provides &lt;code&gt;SDL_SCALEMODE_PIXELART&lt;&#x2F;code&gt; as a scale mode, perfect
for my use cases.&lt;&#x2F;p&gt;
&lt;p&gt;Here is what rendering looked like before applying.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-340&#x2F;.&#x2F;2025-12-06-scale-nearest.png&quot; alt=&quot;Game texture scale mode nearest&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here is what it looked like after.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-340&#x2F;.&#x2F;2025-12-06-scale-pixelart.png&quot; alt=&quot;Game texture scale mode pixel art&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see the &quot;tearing&quot; issue is fixed. The pixelart version is much sharper,
although there is a nice CRT-like aesthetic to the previous scale mode, which I
think was just nearest-neighbor. I&#x27;m going to keep the pixelart scale mode for
a variety of reasons. I won&#x27;t get into that more this time, though.&lt;&#x2F;p&gt;
&lt;p&gt;You may also have noticed that the image of the game seems zoomed in compared
to images or videos in previous posts.
At first, I was scaling the rendering by adding an explicit scale factor (2x)
and then propagating that scale factor to all code that dealt with
transforming to or working in screen pixel space. One question that naturally
comes up is, how do I render to a screen space that has an aspect ratio
different than my world map? If I create a scale factor based on the latest
window dimensions, I will need a horizontal and vertical scale factor. Or, I
could create a uniform scale factor, but what do I do when my scaling doesn&#x27;t
fill the entire screen in some dimension? I&#x27;d generally accommodate this
manually. One would typically fill the gap with
&lt;a href=&quot;https:&#x2F;&#x2F;grokipedia.com&#x2F;page&#x2F;Letterboxing_(filming)&quot;&gt;letterboxes&lt;&#x2F;a&gt;. (Grokipedia
isn&#x27;t so great at the time of this writing in that it doesn&#x27;t show images, and
an image would explain this concept immediately. Anyway, here&#x27;s to hoping the
article is better in the future.)&lt;&#x2F;p&gt;
&lt;p&gt;I found that SDL has a function to handle this for me, called
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_SetRenderLogicalPresentation&quot;&gt;&lt;code&gt;SDL_SetRenderLogicalPresentation&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
So I employed that function. The gist is that you have some actual screen
dimensions that the rendered image is displayed in, but you can configure a
logical render space that you will target your code&#x2F;math to. SDL will handle
scaling your logical render space to the physical render space at presentation
time. So, you don&#x27;t have to worry about handling letterboxing or uniform
scaling anymore. To boot, you also don&#x27;t have to propagate manual scaling
factors throughout your code. Instead, I just propagate the logical render
dimensions. When I want to scale up, I configure the logical render dimensions
to be smaller than the physical render dimensions.&lt;&#x2F;p&gt;
&lt;p&gt;The second notable feature was adding my first test enemy. I hacked the enemy
directly into the game state object for now, but I realized I&#x27;ll have to do
some thinking on how to efficiently manage multiple enemies (generalized to
entities) for some level or game state. Problems include: reusing an entity
asset across multiple entities instead of each entity owning its own
(potentially duplicate) asset, creating a statically-sized collection for
entities and marking some as existent&#x2F;non-existent or having the collection
by dynamically-sized (so non-existence is handled via &lt;code&gt;delete&lt;&#x2F;code&gt; or &lt;code&gt;remove&lt;&#x2F;code&gt;
or something like that on the collection), and how to store information about
how many entities and their kinds to spawn and where for each level.&lt;&#x2F;p&gt;
&lt;p&gt;Then I also need to implement pathfinding for enemies.&lt;&#x2F;p&gt;
&lt;p&gt;Currently I have a simple ghost-like sprite spawned at a hard-coded location
that maybe moves in some random direction when the player moves. As the game
is tile-based, collision detection is simply&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;check for collision on the map&lt;&#x2F;li&gt;
&lt;li&gt;check for collision with other entity&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;video controls preload=&quot;metadata&quot;&gt;
  &lt;source src=&quot;&#x2F;blog&#x2F;2025-340&#x2F;2025-12-06-entity-collision.webm&quot; type=&quot;video&#x2F;webm&quot;&#x2F;&gt;
  Video not supported.
&lt;&#x2F;video&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-11-28</title>
        <published>2025-11-28T15:30:46+00:00</published>
        <updated>2025-11-28T15:30:46+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-332/"/>
        <id>https://lucidvisions.net/blog/2025-332/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-332/">&lt;p&gt;Here is the curated commit log and post for this week&#x27;s game development.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-11-24
&lt;&#x2F;span&gt;&lt;span&gt;chore: move standard-library-like code to src&#x2F;sl (&amp;quot;standard library&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-24
&lt;&#x2F;span&gt;&lt;span&gt;feat: path_concat now maybe adds separators; add test and README
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-24
&lt;&#x2F;span&gt;&lt;span&gt;feat: add string view path_concat overload; extend test
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-25
&lt;&#x2F;span&gt;&lt;span&gt;feat: add arena allocator to standard library
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-25
&lt;&#x2F;span&gt;&lt;span&gt;feat: map loading and path_concat now use an arena
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-25
&lt;&#x2F;span&gt;&lt;span&gt;feat: add arena_realloc and test in preparation for using stb_image
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-25
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce stb_image as library; use our own allocators; load PNGs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-26
&lt;&#x2F;span&gt;&lt;span&gt;feat: replace STBI with SDL_image; load map tilesets as texture atlases
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-26
&lt;&#x2F;span&gt;&lt;span&gt;feat: render actual game map instead of placeholder game map
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-27
&lt;&#x2F;span&gt;&lt;span&gt;feat: move game logic out of main; introduce persist, temp, map arenas
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-28
&lt;&#x2F;span&gt;&lt;span&gt;feat: generalize aseprite_to_map tool to aseprite_asset_exporter
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-28
&lt;&#x2F;span&gt;&lt;span&gt;refactor: extract map into general tileset asset
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-28
&lt;&#x2F;span&gt;&lt;span&gt;feat: load player sprite, add player-facing direction
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Last week&#x27;s commit log query started from the beginning of time in my repo, so
I&#x27;ve added a new filter to the git log query, &lt;code&gt;--since&lt;&#x2F;code&gt;. Again, mostly just
pasting this here for my reference.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;git log --pretty=format:&amp;quot;%ad%n%B&amp;quot; --date=short --since &amp;quot;2025-11-24&amp;quot; --reverse
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As the first commit suggests, I&#x27;ve extracted some features from my game into a
separate library I&#x27;m calling &lt;code&gt;sl&lt;&#x2F;code&gt;, just meaning &quot;standard library.&quot; It&#x27;s
essentially a variety of game-independent utilities that I would typically find
in a languages&#x27; standard library but flavored to my taste.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;path_concat&lt;&#x2F;code&gt;, which I posted about in last week&#x27;s post, will now add path
separators when required. This change is ultimately what led me to introduce
arena allocators which I did shortly after. Without arena allocators, the caller
has to set up enough buffer space for the string concatenation, and the
requirements to set up the buffer correctly grew as the function became more
featureful. Some months ago, in my last group of posts about the game I am
making, I discussed arenas, so I won&#x27;t be going into them here.&lt;&#x2F;p&gt;
&lt;p&gt;At the beginning of the week, I was rendering a placeholder map and character.
I already had Aseprite map loading working in my game last week, but I was not
rendering the map. This week I tossed out the placeholder map and am now
rendering &quot;real&quot; maps. This made testing a lot more fun. It also reminded me
that I need to get a lot better at pixel art :) Anyway, the idea was just to
get something rendered to test the end-to-end art-to-play path.&lt;&#x2F;p&gt;
&lt;p&gt;I previously implemented collision by just checking the tile ID of the tile
the player wanted to move onto. Now, as I probably posted about months ago
as well, I export at least 2 layers from a single map in Aseprite. The first
is the layer containing the graphics for the map, essentially the painted
tiles. The second is the collision layer. I use a single tile, typically red,
in the collision layer to indicate which spaces have collision. Collision in
this case means things like walls. For interactable things in the map, this
will likely be a third layer (or maybe an extension of the collision layer)
which indicates the spawn locations of various entities. I&#x27;ll then create
the entities at runtime and they will particpate in the dynamic game update
loop, not just be static in the map. This is how you can have opening doors,
switches, things like that.&lt;&#x2F;p&gt;
&lt;p&gt;Actually, about arenas, one new thing I did with mine is implement the &lt;code&gt;realloc&lt;&#x2F;code&gt;
behavior described in
&lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;free.3.html&quot;&gt;&lt;code&gt;malloc(3)&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. This was
because I was preparing to use STB image to load tileset PNGs from Aseprite, and
I didn&#x27;t want to implement a PNG parser myself. STB image kindly provides
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nothings&#x2F;stb&#x2F;blob&#x2F;f1c79c02822848a9bed4315b12c8c8f3761e1296&#x2F;stb_image.h#L688&quot;&gt;&lt;code&gt;STBI_REALLOC_SIZED&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
which is like &lt;code&gt;realloc&lt;&#x2F;code&gt; but passes the size of the existing allocation for use
in allocators that don&#x27;t track individual allocation size. This is often the
case for arena allocators, and mine is no different. As you can see in the
commit log, I forgot and then later remembered that SDL provides SDL Image to
do the same thing. Since I&#x27;m already using SDL, I STB image and use SDL Image
instead.&lt;&#x2F;p&gt;
&lt;p&gt;I also moved all of the game logic out of &lt;code&gt;main.cc&lt;&#x2F;code&gt; into its own &lt;code&gt;game.{h|cc}&lt;&#x2F;code&gt;
file. This is to help separate the OS-specific platform portion of the
implementation from the OS-agnostic game logic. I will have to add OS awareness
to things like my standard library, but that will come later. As part of moving
the game logic out of &lt;code&gt;main.cc&lt;&#x2F;code&gt;, I also created a few arenas on the platform
side, bundled them up into a simple &lt;code&gt;Memory&lt;&#x2F;code&gt; struct, and hand them off to
&lt;code&gt;game_update&lt;&#x2F;code&gt; which is the entrypoint for my game logic for a single frame.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, I also replaced rendering of the placeholder character with rendering
of one I drew in Aseprite. I drew 4 sprites for the player: up, down, left,
and right. I could have just exported a sprite sheet (as PNG) and loaded that
into an &lt;code&gt;SDL_Texture&lt;&#x2F;code&gt;, but since I already have an Aseprite-to-binary workflow
with the aforementioned conversion tool, I create the player as a tilemap,
export that, and then re-use my binary-parsing logic to get at the tileset for
the player. Thankfully this doesn&#x27;t waste any memory ultimately. I allocate
the tilemap in the temp arena and just copy out what I need into the persistent
arena. The temp arena gets cleared at the end of the current frame on the
platform side.&lt;&#x2F;p&gt;
&lt;p&gt;There is a bit of a catch, though. Here is a cobbled-together group of struct
definitions from across various game files.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Tileset {
&lt;&#x2F;span&gt;&lt;span&gt;  SDL_Texture&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; atlas;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t atlas_width_tiles;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t atlas_height_tiles;
&lt;&#x2F;span&gt;&lt;span&gt;  uint16_t tile_width_px;
&lt;&#x2F;span&gt;&lt;span&gt;  uint16_t tile_height_px;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Entity {
&lt;&#x2F;span&gt;&lt;span&gt;  EntityType type;
&lt;&#x2F;span&gt;&lt;span&gt;  Vector2f pos;
&lt;&#x2F;span&gt;&lt;span&gt;  Tileset ts;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Player {
&lt;&#x2F;span&gt;&lt;span&gt;  Entity e;
&lt;&#x2F;span&gt;&lt;span&gt;  FacingDirection facing;
&lt;&#x2F;span&gt;&lt;span&gt;  Cooldown move_cooldown;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;GameState {
&lt;&#x2F;span&gt;&lt;span&gt;  uint64_t perf_frequency;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;const char*&lt;&#x2F;span&gt;&lt;span&gt; base_path;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Player player;
&lt;&#x2F;span&gt;&lt;span&gt;  Camera camera;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Map map;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Starting from the bottom of the definitions, &lt;code&gt;GameState&lt;&#x2F;code&gt; is what lives in my
persistent arena. Persistent means the memory survives across frames.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;code&gt;Player&lt;&#x2F;code&gt; is an &lt;code&gt;Entity&lt;&#x2F;code&gt; by composition, and an &lt;code&gt;Entity&lt;&#x2F;code&gt; contains a &lt;code&gt;Tileset&lt;&#x2F;code&gt;
which in turn contains an &lt;code&gt;SDL_Texture&lt;&#x2F;code&gt;. I have not replaced SDL&#x27;s allocators
with my own allocators, and I&#x27;m not sure if I will. This means that I let SDL
use whatever its backing allocators are (not necessarily &lt;code&gt;malloc&lt;&#x2F;code&gt;), but I don&#x27;t
control the memory pool itself. So, things are a little bit fragmented across
memory--I control many allocations in my game logic, but SDL just does whatever
it wants.&lt;&#x2F;p&gt;
&lt;p&gt;The reason I have not replaced SDL&#x27;s allocators is because SDL uses &lt;code&gt;free&lt;&#x2F;code&gt; quite
a lot. I think I might have posted about this before... Anyway, I don&#x27;t want to
write an optimized allocator at this time in my life. When I implemented &quot;free&quot;
for STB image, it was just &lt;code&gt;((void)0)&lt;&#x2F;code&gt;, meaning that I didn&#x27;t actually do
anything, because my arena can&#x27;t free individual allocations.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll see if this hurts me down the road.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, last point for today, about the &lt;code&gt;Tileset&lt;&#x2F;code&gt;. The name of the texture is
&lt;code&gt;atlas&lt;&#x2F;code&gt;. An atlas is just a single texture that may contain multiple tiles.
You get at the tile you want by specifying a subset of the atlas as a
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_RenderTexture#function-parameters&quot;&gt;source rectangle&lt;&#x2F;a&gt;
when rendering. &lt;code&gt;Player::FacingDirection&lt;&#x2F;code&gt; in my case doubles as an index into
the atlas, so when I render a player, I render one of the aforementioned
directional sprites by indexing into the atlas.&lt;&#x2F;p&gt;
&lt;p&gt;In my last implementation, I was loading a unique texture for every sprite.
That may be less efficient at render time if the allocations for the sprites
live far away from each other in memory. That, and it&#x27;s just more things to
keep track of. &lt;code&gt;Map&lt;&#x2F;code&gt; also uses &lt;code&gt;Tileset&lt;&#x2F;code&gt;, so atlases benefit us there, too,
where there are many more sprites than there typically are for entities like
players.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a demo. Pardon the programmer art.&lt;&#x2F;p&gt;
&lt;video controls preload=&quot;metadata&quot;&gt;
  &lt;source src=&quot;&#x2F;blog&#x2F;2025-332&#x2F;2025-11-28-weekly-demo.webm&quot; type=&quot;video&#x2F;webm&quot;&#x2F;&gt;
  Video not supported.
&lt;&#x2F;video&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-11-27</title>
        <published>2025-11-27T09:28:13+00:00</published>
        <updated>2025-11-27T09:28:13+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-331/"/>
        <id>https://lucidvisions.net/blog/2025-331/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-331/">&lt;p&gt;The year is 750 AD. It&#x27;s central Japan, the
&lt;a href=&quot;https:&#x2F;&#x2F;grokipedia.com&#x2F;page&#x2F;Nara_period&quot;&gt;Nara Period&lt;&#x2F;a&gt;, the tail end of
winter. The cold is biting.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s early in the morning. The sun has just started to peak over the mountains
and through the trees. You&#x27;re outside, wrapped in multiple layers, shivering.
But you don&#x27;t think about the cold. There are tens of other people around you
doing the same. Everyone is facing a single person standing before everyone
else. Let&#x27;s call him the foreman. The foreman is giving out the instructions
for the day. People will split into teams and go here and there. Everyone&#x27;s
general task is to cut down trees and prepare planks from them for carpenters
to further refine.&lt;&#x2F;p&gt;
&lt;p&gt;You pick up your tools, hands hardened from all the work you&#x27;ve done up until
now. You move with your team to your designated area. You self-organize into
smaller units, some working in tandem to cut trees, others cutting fallen trees
into logs, stripping bark and branches off of them. You get stuck in the group
that has to chop the trees down. You and your partners gather around various
trees with axes, each chopping away. You had an idea to create a long sawblade
that two people could hold, one on each end, and you could fell trees faster
with less work individually. It&#x27;ll have to wait, though, as you can&#x27;t create
that on the spot. You wished you were in the team stripping logs or hauling them
back to camp. Chopping down trees is hard work, and your team is the bottleneck.
The others are resting while waiting for another tree to log and haul.&lt;&#x2F;p&gt;
&lt;p&gt;As you work, your body heats up, and you start to sweat. You remove a layer.
It&#x27;s uncomfortable, and you can smell the odor of your teammates as well. But
these trees aren&#x27;t going to fell themselves, and you can&#x27;t let everyone else
down. You try not to think about tomorrow, but you know you&#x27;ll be back to do
the same thing all over again. At least you&#x27;ll get to rotate to a different
responsibility.&lt;&#x2F;p&gt;
&lt;p&gt;6 months pass. You haven&#x27;t always been felling trees for this time, but you&#x27;ve
been doing similar manual labor otherwise. You think about the nobles who have
come through town while you work. Ah, to be like them and not have to work so
hard. Whatever, どうでも良い.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s the middle of summer now, and its sweltering hot. You sit on cool stone in
the middle of the day, unlike your days prior toiling away when it was colder.
You&#x27;re shaded from the midday sun by a large wooden overhang that is just one
part of a large castle-temple complex. Last year this overhang wasn&#x27;t here, and
everyone felt its lack literally. You remember getting caught in the summer rain
brought by the typhoons. It wasn&#x27;t even refreshing. The air was so humid, and
the rain was somehow warm. You were hot and miserable, and everyone smelled.&lt;&#x2F;p&gt;
&lt;p&gt;But this year is different. You sit under a tangible structure protecting you
from the elements. Many others do the same. You weren&#x27;t the carpenter, you
weren&#x27;t the architect, you aren&#x27;t a noble, but everyone knows that you pitched
in and is thankful for that. It starts to rain. You and your buddies look out
into the damp heat and then look at each other, smile, and give a sigh of
relief.&lt;&#x2F;p&gt;
&lt;p&gt;Summer transitions into fall. It&#x27;s cooler now, and you&#x27;re back to work, moving
large stones from a quarry to a mason&#x27;s workshop. The mason does his thing, and
then you move shaped stones elsewhere around the castle-temple complex. It&#x27;s
monotonous drudgery. You wish you could be out helping your parents plant or
pick rice like you did when you were 8 years old, daydreaming about goofing off
with your friends later on or listening to some bizarre stories and wondering
about the world. Whatever, どうでも良い. These stones aren&#x27;t going to move
themselves.&lt;&#x2F;p&gt;
&lt;p&gt;The next winter comes, and the cold is once again biting. The town doesn&#x27;t need
as much wood this time around, so you&#x27;ve moved on to other tasks, doing grunt
work on farms, for smiths, for masons. This year the cold is more pronounced,
though, since you have something to compare it to. You and your buddies look
out into the snow falling around you, recalling simultaneously freezing and
sweating this time last year. You&#x27;re a little too hot, so you lift yourself
out of the &lt;a href=&quot;https:&#x2F;&#x2F;grokipedia.com&#x2F;page&#x2F;Onsen&quot;&gt;onsen&lt;&#x2F;a&gt;, and steam billows from
your body like you&#x27;re some sort of deity. You feel like one, at least, knowing
that the blood and sweat you put into all that work with wood and stone has
provided for everyone around you, and everyone else knows it too. You built
something that has measurably improved your own life and the lives of people
around you, and you presumably get to enjoy it for the rest of your days.&lt;&#x2F;p&gt;
&lt;p&gt;The year is 2025 AD. It&#x27;s Tokyo, the
&lt;a href=&quot;https:&#x2F;&#x2F;grokipedia.com&#x2F;page&#x2F;Reiwa_era&quot;&gt;Reiwa Period&lt;&#x2F;a&gt;, the tail end of winter.
The cold is biting.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s early in the morning. The sun has just started to peak over the mountains
and through the trees. You&#x27;re outside, wrapped in multiple layers, shivering.
You think about the cold. There are tens of other people around you doing the
same. Everyone is facing a single person standing before everyone else. Let&#x27;s
call it the crossing signal. The signal is giving out the instructions for when
to walk. At work, people are split into teams. Everyone&#x27;s general task is to
move information from one service the company pays for to another service the
company pays for.&lt;&#x2F;p&gt;
&lt;p&gt;You unlock your computer, heart hardened from all the work you&#x27;ve done up until
now. You check-in with your team on your designated Slack channel. You
self-organize into smaller units, some working in tandem to address this
problem, others essentially creating new problems. You get stuck in the group
that has to fix problems. You and your partners gather around various documents,
each cross-checking, debugging. You had an idea to create an automated test
barrage, so you could catch issues earlier, faster, with less work individually.
It&#x27;ll have to wait, though, as you can&#x27;t create that on the spot. Thankfully
they run the heater late into the night, so you can stay late and develop it on
your own time. You wished you were in the team creating new problems for
everyone else. At least they get to create puzzles for themselves and then code
up solutions. Fixing problems is hard work, and your team is the bottleneck.
Management is waiting for another fix to send to the customer.&lt;&#x2F;p&gt;
&lt;p&gt;As you work, your body heats up, and you start to sweat. You remove a layer.
It&#x27;s uncomfortable, and you can smell the odor of your teammates as well. But
these tasks aren&#x27;t going to finish themselves, and you can&#x27;t let everyone else
down. You try not to think about tomorrow, but you know you&#x27;ll be back to do
the same thing all over again. At least next quarter you&#x27;ll get to rotate to
a different responsibility.&lt;&#x2F;p&gt;
&lt;p&gt;6 months pass. You haven&#x27;t always been fixing problems for this time, but you&#x27;ve
been doing similar labor otherwise. You think about the managers who have
come through the org while you work. Ah, to be like them and not have to work so
hard. Whatever, しようがない.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s the middle of summer now, and its sweltering hot. You sit in a
climate-controlled office in the middle of the day, just like your days prior
toiling away when it was colder. You&#x27;re shaded from the midday sun by a large,
sterile ceiling that&#x27;s just one part of your company&#x27;s massive office. Last year
this ceiling was there, and you didn&#x27;t even think about it. You were caught
in the summer rain brought by the typhoons on your way in. It wasn&#x27;t even
refreshing. The air was so humid, and the rain was somehow warm. You were hot
and miserable, and everyone smelled.&lt;&#x2F;p&gt;
&lt;p&gt;This year is no different. You sit under a tangible structure protecting you
from the elements. Many others do the same. You weren&#x27;t the carpenter, you
weren&#x27;t the architect, you aren&#x27;t a manager, and you didn&#x27;t pitch in. No one
even takes a moment to be thankful for the structure. It starts to rain. You
and your buddies don&#x27;t even look out into the damp heat, are fixated on meeting
deadlines for people they don&#x27;t care about, building something they&#x27;ll never
use, for people that will never know them, if the product isn&#x27;t scrapped on the
whim of someone entirely disconnected from the work.&lt;&#x2F;p&gt;
&lt;p&gt;Summer transitions into fall. It&#x27;s cooler now, and you&#x27;re on a different
project. The teams around you do their thing, and then you integrate it. It&#x27;s
monotonous drudgery. You wish you could be out helping your parents garden like
you did when you were 8 years old, daydreaming about goofing off with your
friends later on or playing some fantasy RPG and wondering about that world.
Whatever, どうでも良い. These tasks aren&#x27;t going to complete themselves.&lt;&#x2F;p&gt;
&lt;p&gt;The next winter comes, and the cold is once again biting. Your org didn&#x27;t need
as many people this time around, so you&#x27;ve moved on to other tasks, doing grunt
work for the next project. This year the cold is no more pronounced. You look
out the window into the snow falling, recalling nothing. You&#x27;re a little too hot
so you take your jacket off. Steam billows from your coffee like some sort of
demon. It feels like one, at least, knowing that you depend on it to keep you
going, pouring sweat and life into all that thankless work you have to do.
Everyone knows it, so you tell yourself its okay, but they don&#x27;t care, because
that&#x27;s just life, isn&#x27;t it? You built something that has measurably improved
nothing, and you don&#x27;t even get to enjoy it, because you never really needed it
anyway. And you never will for the rest of your days.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-11-24</title>
        <published>2025-11-24T09:48:00+00:00</published>
        <updated>2025-11-24T09:48:00+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-328/"/>
        <id>https://lucidvisions.net/blog/2025-328/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-328/">&lt;p&gt;I had joined a startup a few months back, but I recently left. Back to my own
thing at this time in my life.&lt;&#x2F;p&gt;
&lt;p&gt;When I first left industry to do my own thing, so many people asked me what was
next for me and were surprised by &quot;doing my own thing&quot; that I had to refine my
answer to make the inevitable same-conversation-many-times efficient. The common
question to me was, &quot;what would it take for me to work for someone else again?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;My answer was, simply needing money aside, three criteria.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The company is a small group of experts.&lt;&#x2F;li&gt;
&lt;li&gt;The group has a single, measurable mission.&lt;&#x2F;li&gt;
&lt;li&gt;The mission and tasks resonate with me.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I&#x27;ve since added a fourth criteria: everyone is humble.&lt;&#x2F;p&gt;
&lt;p&gt;Also, I&#x27;ve created a new rule for myself for any interview I participate in from
here on out. That rule is, if I want to join a company, I need to first come up
with my own plan and explanation of how I would accomplish their mission,
end-to-end, meaning from hiring people and starting with an empty text file in
front of me as a software engineer, to shipping and maintenance. It&#x27;s most
important to run this by company leaders to get a sense for their familiarity
and experience in the domain and also to get an early read on points I might
disagree with or things I might be wrong about and expectations I need to
adjust.&lt;&#x2F;p&gt;
&lt;p&gt;With that out of the way, here is my next development update. I was previously
writing development logs mostly daily, but now I think I will do so weekly,
unless there is some singular topic I want to on about at length. It&#x27;s Monday
already, but I&#x27;ll be posting about last week. Also, it&#x27;s a three-day weekend
here in Japan, but when you work for yourself and have no income yet, there is
no such thing.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll experiment with posting a slim log and then touching on this and that. The
command I&#x27;m using to print the log is&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;git log --pretty=format:&amp;quot;%ad%n%B&amp;quot; --date=short --reverse
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-11-15
&lt;&#x2F;span&gt;&lt;span&gt;docs: initial commit
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-15
&lt;&#x2F;span&gt;&lt;span&gt;feat: open a window, attempt FPS target
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-17
&lt;&#x2F;span&gt;&lt;span&gt;feat: render a simple map the size of the window; add build docs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-17
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce create&#x2F;destroy entity function; create player
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-17
&lt;&#x2F;span&gt;&lt;span&gt;feat: implement basic movement and collision
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-18
&lt;&#x2F;span&gt;&lt;span&gt;tweak: double running speed in tiles per second
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-18
&lt;&#x2F;span&gt;&lt;span&gt;feat: introduce GameMap struct
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-19
&lt;&#x2F;span&gt;&lt;span&gt;feat: add camera that follows player; also add clip rect on main window
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;This fixes a rendering slowdown caused by extending the map size in an
&lt;&#x2F;span&gt;&lt;span&gt;earlier commit and rendering the entire map--even portions out of the
&lt;&#x2F;span&gt;&lt;span&gt;frustum--on each frame.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-19
&lt;&#x2F;span&gt;&lt;span&gt;docs: index documentation and add docs on coordinate systems in use
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-20
&lt;&#x2F;span&gt;&lt;span&gt;chore: switch tile size to 16 pixels to anticipate maps; extend docs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-21
&lt;&#x2F;span&gt;&lt;span&gt;feat: add small paths and strings library
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-21
&lt;&#x2F;span&gt;&lt;span&gt;feat: add aseprite to binary map conversion wrapper tool
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-21
&lt;&#x2F;span&gt;&lt;span&gt;feat: add a map parser; this commit does not include assets&#x2F;, data&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;2025-11-21
&lt;&#x2F;span&gt;&lt;span&gt;feat: add string view and parent path utilities
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I restarted the codebase. I did this mostly to refamiliarize myself with what
will become the entire implementation. I&#x27;m still using SDL3 and, by consequence,
CMake. I considered switching to Jai or Odin but stuck with my favorite dialect
of &quot;C++,&quot; which is C with operator and function overloading. Aside,
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=ES5_IWG8rcw&quot;&gt;this podcast&lt;&#x2F;a&gt; had some hilarious
parts, where the host was hoping for hot debate around what C could do better
and where it shines, didn&#x27;t get it, and was getting jokingly visibly frustrated
when the guests mostly kept agreeing with each other about how bad C was and why
they nonetheless stick to most of its core principles.&lt;&#x2F;p&gt;
&lt;p&gt;Currently the game is entirely tile-based with tile-based positions and
movement. Entities are just a struct of a position and sprite, in that order.
Collision is a simple can-move-to-world-tile check at this time.&lt;&#x2F;p&gt;
&lt;p&gt;I had never implemented a camera before, so that was interesting. Being real
with you, it took me an embarassingly long amount of time to conceptualize the
transformation in my head. My understanding is that there are two common types
of camera setups in top-down 2D games, and they differ in how the camera
position is tracked. One setup tracks the camera position in screen pixels as
the top-left corner of the screen (if your render system places (0, 0) at the
top-left of the screen. The other setup tracks the camera position in screen
pixels as the center of the screen. In my case, I track it at the center of
the screen. This is so that if I want to &quot;watch&quot; any particular tile or entity,
all I need to do is set the camera to that entity&#x27;s position. Currently I do
so for the player, so the camera tracks the player as the player moves.&lt;&#x2F;p&gt;
&lt;p&gt;Regarding the transformation, consider the following scenario:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;+-------+(0,0) px
&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;v                        Screen
&lt;&#x2F;span&gt;&lt;span&gt;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
&lt;&#x2F;span&gt;&lt;span&gt;X  |                                                        X
&lt;&#x2F;span&gt;&lt;span&gt;X------------------ screen width ---------------------------X
&lt;&#x2F;span&gt;&lt;span&gt;X  |                                                        X      Game Map
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     +----------------------------------X----------------+
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                 o                X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                 ^                X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                 |                X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  | screen              |                 +---+tile to     X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  | height              |    o                 render on   X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |    ^                 screen      X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |    |                             X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |    +---+camera pos               X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;X  |                     |                                  X                |
&lt;&#x2F;span&gt;&lt;span&gt;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                |
&lt;&#x2F;span&gt;&lt;span&gt;                         |                                                   |
&lt;&#x2F;span&gt;&lt;span&gt;                         |                                                   |
&lt;&#x2F;span&gt;&lt;span&gt;                         |                                                   |
&lt;&#x2F;span&gt;&lt;span&gt;                         |                                                   |
&lt;&#x2F;span&gt;&lt;span&gt;                         +---------------------------------------------------+
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Assume we want to render the point labeled &quot;tile to render on screen.&quot; To do
this, we need to find that tile&#x27;s position in screen space. The tile has a
position in game-map tile space (Px, Py). The camera has a special property
of having a position known in two frames. It has a game-map position (Cx, Cy),
and we also know it has a fixed position in screen space in the center, which is
(screen width &#x2F; 2, screen height &#x2F; 2). The formula to transform the tile to
render from game-map space it screen space (Psx, Psy) is thus&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;(Psx, Psy) = ((Px, Py) - (Cx, Cy)) * (tile width px, tile height px) +
&lt;&#x2F;span&gt;&lt;span&gt;             (screen width &#x2F; 2, screen height &#x2F; 2)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;(Px, Py) - (Cx, Cy)&lt;&#x2F;code&gt; gets the tile position in game map space relative to our
camera position. If the camera were at (0, 0) in game map space, the position
relative to the camera would simply be &lt;code&gt;(Px, Py)&lt;&#x2F;code&gt;. Multiplying by &lt;code&gt;(tile width px, tile height px)&lt;&#x2F;code&gt; transforms the game-map tile coordinates into game-map
pixel coordinates (the coordinate now points to the upper-left corner of a
particular tile). Finally, to transform game-map pixel coordinates into screen
pixel coordinates, we need to add the camera offset. Again, if the camera were
at &lt;code&gt;(0, 0)&lt;&#x2F;code&gt;, we would be done.&lt;&#x2F;p&gt;
&lt;p&gt;This transform can produce coordinates outside of the visible screen space. As
one of my commit messages above suggests, if we don&#x27;t avoid rendering outside
of the screen space, we will pay the cost of rendering. I found with SDL3 that
even if I set the render clip rectangle to the screen space, I still run over
my frame times. So, I also have some logic to determine the subset of the game
map to consider for drawing to the screen on a given frame, and then I apply
the transform shown above to convert visible tiles to screen space.&lt;&#x2F;p&gt;
&lt;p&gt;On to game map design, I will stick with pixel art and Aseprite. I made a small
patch to my Aseprite tilemap binary exporter to adjust the exported file names,
and then I introduced a Python utility on my game-code side to transform
&lt;code&gt;.aseprite&lt;&#x2F;code&gt; files to binary map files. The script ultimately transforms as
follows:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;test-map.aseprite -&amp;gt; .
&lt;&#x2F;span&gt;&lt;span&gt;                     ├── test-map&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;                     │   ├── tileset1.png
&lt;&#x2F;span&gt;&lt;span&gt;                     │   └── tileset2.png
&lt;&#x2F;span&gt;&lt;span&gt;                     └── test-map.bin
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;test-map.bin&lt;&#x2F;code&gt; is what my game code parses to load in map data, and resources
corresponding to &quot;test-map&quot; are stored in a folder of the same name peer to the
binary file. Paths in &lt;code&gt;test-map.bin&lt;&#x2F;code&gt; are relative to the location of
&lt;code&gt;test-map.bin&lt;&#x2F;code&gt;. Avoiding absolute paths in this case is for portability,
especially since the resource location on disk in the source tree is different
than the location resources are placed after a build. I put my resources in a
&lt;code&gt;data&lt;&#x2F;code&gt; folder, map resources in &lt;code&gt;data&#x2F;maps&lt;&#x2F;code&gt; in particular. To accomodate
relocation in CMake, I have&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;cmake&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-cmake &quot;&gt;&lt;code class=&quot;language-cmake&quot; data-lang=&quot;cmake&quot;&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;add_custom_command&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;TARGET &lt;&#x2F;span&gt;&lt;span&gt;the_target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;POST_BUILD
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;COMMAND &lt;&#x2F;span&gt;&lt;span&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;CMAKE_COMMAND&lt;&#x2F;span&gt;&lt;span&gt;} -E copy_directory_if_different
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;CMAKE_SOURCE_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;}&#x2F;data&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;$&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;TARGET_FILE_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;the_target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;gt;&#x2F;data&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;add_custom_command&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;TARGET &lt;&#x2F;span&gt;&lt;span&gt;the_target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;POST_BUILD
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;COMMAND &lt;&#x2F;span&gt;&lt;span&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;CMAKE_COMMAND&lt;&#x2F;span&gt;&lt;span&gt;} -E copy_directory_if_different
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;CMAKE_SOURCE_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;}&#x2F;data&#x2F;maps&#x2F;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;$&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;TARGET_FILE_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;the_target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;gt;&#x2F;data&#x2F;maps&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This requires CMake 3.26. The annoying thing is that
&lt;code&gt;copy_directory_if_different&lt;&#x2F;code&gt; does not do a recursive comparison, so I need to
check the top-level &lt;code&gt;data&lt;&#x2F;code&gt; directory and &lt;code&gt;data&#x2F;maps&lt;&#x2F;code&gt; directory explicitly. Also,
content removal from the source directory does not count as a difference,
meaning that previously-removed resources can linger around in the build output.
I could remove and re-copy all resources on every build, but until I hit some
gotcha, using an old resource by accident, I won&#x27;t bother.&lt;&#x2F;p&gt;
&lt;p&gt;SDL3 provides a convenient
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_GetBasePath&quot;&gt;&lt;code&gt;SDL_GetBasePath&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which allows
me to get the path the executable is run from. This allows me to then discover
my executable-relative resource directory and correctly resolve the relative
paths in the map binary files.&lt;&#x2F;p&gt;
&lt;p&gt;Something like &lt;code&gt;std::filesystem&lt;&#x2F;code&gt; would be handy here, but I&#x27;m avoid &lt;code&gt;std::&lt;&#x2F;code&gt; as
much as I can, so I have reimplemented just what I need. Toward this, I
introduced a simple string view utility. It&#x27;s trivially defined as&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;StringView {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;char*&lt;&#x2F;span&gt;&lt;span&gt; str;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t len;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;where the characters are ASCII-only and the &lt;code&gt;len&lt;&#x2F;code&gt; portion of the backing string
is not guaranteed to contain a null terminator. We&#x27;ll see how useful this turns
out to be. I mostly introduced it because I have not introduced my scratch
allocator yet. If I had the latter, I&#x27;d allocate without thinking much about it,
knowing the memory I allocated will be reclaimed shortly after.&lt;&#x2F;p&gt;
&lt;p&gt;Toward what I need from &lt;code&gt;std::filesystem&lt;&#x2F;code&gt;, I introduced &lt;code&gt;path_concat&lt;&#x2F;code&gt; and
&lt;code&gt;path_parent&lt;&#x2F;code&gt; functions. &lt;code&gt;path_parent&lt;&#x2F;code&gt; finds the parent in a provided path.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span&gt;StringView &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;path_parent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;const char* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;, size_t &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This also took me an embarassingly long amount of time to write elegantly. I
won&#x27;t share the code in the event you want to challenge yourself. (By the way,
AI output was rather bloated and inefficient at the time of this writing.) My
implementation only handles Unix path separators at this time, so this will be
one implementation I need to port when going to Windows. Here are the test
cases I pass, if you want to take a go at it yourself.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;TestCase { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;const char*&lt;&#x2F;span&gt;&lt;span&gt; input; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;const char*&lt;&#x2F;span&gt;&lt;span&gt; expected; };
&lt;&#x2F;span&gt;&lt;span&gt;TestCase cases[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&#x2F;c&#x2F;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, },
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&#x2F;&#x2F;c&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;&#x2F;b&#x2F;c&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;&#x2F;b&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&#x2F;c&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&#x2F;c&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&#x2F;b&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;a&#x2F;b&#x2F;c&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;a&#x2F;b&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;a&#x2F;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;.&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;ab&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;.&#x2F;a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;.&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&#x2F;a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;.&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Speaking of going to Windows, one reason I&#x27;m using SDL3 is to allow myself to
develop on Linux and macOS and &quot;just&quot; test on Windows. We&#x27;ll see how that goes.
I really like the idea of being able to bring my macBook wherever with me and
still develop and play my game, don&#x27;t really have a decent portable option with
Windows at this time.&lt;&#x2F;p&gt;
&lt;p&gt;Regarding map loading and whatnot, I may ultimately bake the map data into the
final binary. I may either use the recent
&lt;a href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;preprocessor&#x2F;embed.html#Resources&quot;&gt;&lt;code&gt;#embed&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
or, more than likely, just implement a tool myself to achieve the same.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-10-15</title>
        <published>2025-10-15T08:52:35+00:00</published>
        <updated>2025-10-15T08:52:35+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-288/"/>
        <id>https://lucidvisions.net/blog/2025-288/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-288/">&lt;p&gt;I heard a quote yesterday--I think it was from Chris Bumstead--along the lines
of&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;What used to drive me now exhausts me.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I think it very succinctly captures my feelings about software development at
this stage of my life.&lt;&#x2F;p&gt;
&lt;p&gt;I used to study programming for programming&#x27;s sake. All you had to say was,
&quot;did you know?&quot; and you had my attention for an entire weekend, studying more
sophisitcated ways to do the same old thing, diving into arcana, reciting the
new mantras. Because I liked
&lt;a href=&quot;https:&#x2F;&#x2F;youtube.com&#x2F;shorts&#x2F;GYlHx4DZtiY?si=XRQIWiR75zDkmnFQ&quot;&gt;puzzles&lt;&#x2F;a&gt;. Because
I wanted to be in the group that knew. &quot;Did you know&quot; is not a question anyone
asks to actually learn anything. It&#x27;s a lead-in to a flex. Virtue signaling.
A waste of time. Studying the esoterica of physics has an end and ultimately
leads to simple solutions for everyone. I find that studying the esoterica of
most modern software leaves one prone to normalize and even perpetuate it.&lt;&#x2F;p&gt;
&lt;p&gt;Now the arcana exhausts me. It&#x27;s mostly mental gyration, or even worse, just
puzzles we have created for ourselves that get in the way of making meaningful
progress for the species.&lt;&#x2F;p&gt;
&lt;p&gt;Use as few tricks as possible.&lt;br &#x2F;&gt;
Write as little code as possible.&lt;br &#x2F;&gt;
Add as few abstractions as possible.&lt;&#x2F;p&gt;
&lt;p&gt;Software engineering is a means to an end.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve normalized &quot;best practice&quot; as a header file, an implementation file,
object-oriented whatever, 5 different kinds of constructors, meta-programmed
interfaces that help account for non-ref, ref, const ref, universal (lol) ref,
etc. Newcomers to the field look at the awful tools and think, mastery of this
is what it means to be great. The tools are so complicated that tool use itself
was reified into meaningful work when no one was looking. This is why competent
leadership in a company is so important--you need leaders who can discriminate
between someone who knows everything about hammers and someone who knows which
to reach for to build something.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to transform your ego into code, that&#x27;s fine, but keep both off of
the critical path of something we&#x27;re all supposed to use in the end. Physics
doesn&#x27;t care about your ego, so I&#x27;d like to keep ego out of software engineering
for the species as well.&lt;&#x2F;p&gt;
&lt;p&gt;Did &lt;em&gt;you&lt;&#x2F;em&gt; know you can do pretty much everything with structs, function
overloading, and operator overloading?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-208</title>
        <published>2025-07-27T12:20:08+00:00</published>
        <updated>2025-07-27T12:20:08+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-208/"/>
        <id>https://lucidvisions.net/blog/2025-208/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-208/">&lt;p&gt;A Japanese person posted the following question in English on a language
exchange application I use: &quot;What makes someone feel romantic love for another
person?&quot; I&#x27;ve wanted to be able to discuss things like &quot;love&quot; in Japanese, so
this seemed like a good opportunity for me to learn in context. My Japanese is
still insufficient, but I&#x27;m sharing my reply here, in part because it does
capture the essence I&#x27;ve come to understand of love, and in part to act as a
benchmark of how far I&#x27;ve come with Japanese and how much further I still have
to go.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;この投稿をありがとう！
私の意見を日本語でよく説明できるかどうかわからないけど、頑張る笑&lt;&#x2F;p&gt;
&lt;p&gt;「ラブ」は人によって意味が違うね。
例えば、昔にギリシャ人はラブを三種類に別れた。
この投稿に関している「ラブ」の種類は二つがあると思う（ギリシャの分類とよく合わない）。&lt;&#x2F;p&gt;
&lt;p&gt;先ずはロマンチックなこと、つまり恋。
恋は相手の見た目、言動、地位、肉体的な相性などに関しているものだ。
恋の原因は先天的（例えば遺伝）と後天的（例えば文化、環境、社会）な要因だと思う。
このラブは二人がいて、私と私の相手だね。
カップルは関係のために取引している。
私は何かをあげる、例えば親切な言葉や時間や思いやりな贈り物や恋愛だ。
相手も適当なことをくれる。
もちろん、いい関係にはこの取引が仕事のようなことじゃなくて、カップルは互いにこうしたいから自然なことだね。
でも結局二人、私と私の相手、がいる。&lt;&#x2F;p&gt;
&lt;p&gt;一方、愛ということもあるね。
これは一体感を探すことだ。
愛の原因は、なんとか相手の中に自分自身を見ることだ。
恋の二人より、愛は一人だけがいて、「私たち」だ。
相手にちゃんと自分自身を見るために、自分自身をよくわかって愛することが必要だと思う。
もちろん相手は自分の興味や目標があるけど、相手の中に自分自身を見るから自然に心の底から応援できるようになる、もう自分のことを愛しているのおかげで。
これは愛の逆説でしょう、相手を愛するようになるために、先ずは自分のことを愛しなくちゃいけない。
自分のことを愛するために、先ずは自分のことをわからなくちゃいけない。
自分をわかるというのは、自分の目標や価値観や前のトラウマを解けることなどだ。&lt;&#x2F;p&gt;
&lt;p&gt;恋の始まりは外に見ることによる。
愛の始まりは中に見ることによる。
恋の方がわかりやすい、映画や曲やソーシャルメディアでイメージが多いから。
愛をわかるの方が難しい、あまり誰にも自分の心の底を見えないから。
（例えば両親の方が他の人より自分の子供の心をわかりやすいと思う。）
もちろん、人は年に取るにつれて変える、でも愛の原因のおかげで、カップルが一緒に変えることもできると思う。&lt;&#x2F;p&gt;
&lt;p&gt;私にとって、いい関係をできるために、恋と愛とどっちも必要だと思う。
英語ではこのラブの種類について説明しかたもちろんあるけど、あまり特別の言葉じゃない（日本語とかギリシャ語より）。
私の返信のために新し言葉と表現をならなくちゃいけなかったから、もし日本語の言葉のニュアンスを誤解したなら、すみませんね！
ここまで読んでくれてありがとうございます！
またおもしろう投稿もありがとう！&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Posted after realizing I left out another thing I wanted to share:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;ところで、いい関係の基準について意見をシェアしたいと思っている。
関係は履歴証で並んでいる項目のようなことに基けば基づくほど、不安定になるの可能性が高いと思う。
給料、仕事、趣味、実績、どんな学校など、これに興味があるのは問題ない。
でもこんなものは相手の性格じゃなくて、相手の性格の結果でしょう。
私にとって、適当な基準は相手と一緒にいる時心地いいや相手を深く信じられる、ユーモアが好きや相手の親切さか思いやりが好きなど。
こんなことの方が説明するのは難しいけど、恋より人生のどの時期においてもより安定していると思う。
それにしても、恋も大事なものでしょう。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-191</title>
        <published>2025-07-10T10:40:06+00:00</published>
        <updated>2025-07-10T10:40:06+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-191/"/>
        <id>https://lucidvisions.net/blog/2025-191/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-191/">&lt;p&gt;Since I last wrote, I extended the bump allocator from
&lt;a href=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-189&#x2F;&quot;&gt;last time&lt;&#x2F;a&gt; to also provide storage persistent across
frames. This is necessary to contain at least two things: the general game state
struct and tilemap indices. Both of these need to persist across frames.
Previously, I was instantiating the game state on the platform side and passing
a pointer to it into my game-update function. The platform is otherwise
game-state-agnostic, though, so this didn&#x27;t make much sense. For the tilemap
indices, I was just &lt;code&gt;malloc&lt;&#x2F;code&gt;&#x27;ing space for them. My recent goal was to remove
all instances of &lt;code&gt;malloc&lt;&#x2F;code&gt; from the game logic-side code.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the allocator type definition looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Allocator {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void*&lt;&#x2F;span&gt;&lt;span&gt; base_temp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nullptr;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t size_temp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t used_temp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; Implementation detail: `base_persist` shall point to the beginning of the
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; allocated memory.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void*&lt;&#x2F;span&gt;&lt;span&gt; base_persist &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nullptr;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t size_persist &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t used_persist &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The implementation detail is because I allocate a single region of memory for
both types of storage. The temporary storage starts &lt;code&gt;size_persist&lt;&#x2F;code&gt; bytes after
&lt;code&gt;base_persist&lt;&#x2F;code&gt;. I also modified the API to separate allocations to temporary
storage and persistent storage and to only provide the reset ability on
temporary storage. Besides resetting, there is still no general ability to
free allocated memory, meaning that allocations in persistent storage are
expected to live for the lifetime of the program runtime.&lt;&#x2F;p&gt;
&lt;p&gt;The platform side sets up the allocator and its memory and passes it to my
game update function. Perhaps a bit dirty, but although I am dealing with
an &lt;em&gt;allocator&lt;&#x2F;em&gt;, it really is my game&#x27;s memory pool with functions to manage
the memory. Recall from the previous post that I do use the temporary storage
a bit on the platform side to build a couple strings. This is no problem since
no one should rely on exact positions of anything in temporary storage.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, I require that the platform never allocate from persistent
storage the game will use. This way, my game can always assume its game state
object lives at the beginning of persistent storage. At the beginning of each
pass through my game update function, I get at the game state via:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span&gt;GameState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; gs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(GameState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)allocator-&amp;gt;base_persist;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;My game state object has an &lt;code&gt;initialized&lt;&#x2F;code&gt; flag that tells me whether I need
to actually set it up or not. Because I use &lt;code&gt;mmap&lt;&#x2F;code&gt; to allocate the backing
storage, the storage is guaranteed to be zeroed after allocation. As a sanity
check, I make sure that if the game state is unintialized that
&lt;code&gt;allocator-&amp;gt;used_persist&lt;&#x2F;code&gt; is also &lt;code&gt;0&lt;&#x2F;code&gt;. I then stash pointers to this and
that in persistent storage in my game state instance, and from there I can
navigate the game memory across frames.&lt;&#x2F;p&gt;
&lt;p&gt;When initializing a tilemap, I previously dynamically allocated space for as
many tilesets and layers as I needed based on what I discovered in my
binary-encoded tilemap. Now, I set up storage for some fixed number of these
things in persistent storage and point a tilemap instance at these persistent
resources during tilemap initialization. This way I can avoid dynamic allocation
entirely in the game logic, at the expense of allocating more memory up front
than I may ultimately need. I can tune the allocations as the game matures.&lt;&#x2F;p&gt;
&lt;p&gt;Right now my game update function interface looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * Update the game state based on input.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param a The allocator containing the memory for the game. The game requires
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *          that the provider does not modify permanent storage in any way.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param input The input to use to update the game state, assumed to be input
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *              toward the next frame to render.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param renderer The rendering destination for all graphics updates.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;game_update&lt;&#x2F;span&gt;&lt;span&gt;(Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, Buttons&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;, SDL_Renderer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;renderer&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At first I thought it would be nice to keep SDL things out of the game logic
code, but SDL itself is supposed to be my operating system abstraction, so I&#x27;m
fine bringing it into the game logic code. I do call SDL routines that
(de)allocate, for example creating or destroying textures. To avoid this, I
would need to build a graphics abstraction on the game logic side, probably
via various memory buffers allocated on the platform side and passed to the
game update function, have the game code write into those buffers, and then
have the platform side move content from those buffers into SDL resources. But
that is just abstraction on top of the thing I&#x27;m using for abstraction, which
is not the way to go.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-189</title>
        <published>2025-07-08T16:47:44+00:00</published>
        <updated>2025-07-08T16:47:44+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-189/"/>
        <id>https://lucidvisions.net/blog/2025-189/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-189/">&lt;p&gt;As an unintentional follow-up to &lt;a href=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-188&#x2F;&quot;&gt;yesterday&#x27;s post&lt;&#x2F;a&gt;, I
ended up writing a simple bump allocator. The motivation was that I wanted to
build a routine to convert relative paths to absolute paths. For this, I
need a place to store the absolute path. The relative paths are relative to
the folder the game binary sits in, so I can&#x27;t predict the full string length of
the absolute path, meaning I need to dynamically allocate.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the API for the bump allocator; no surprises if you&#x27;ve seen one before.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Allocator {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void*&lt;&#x2F;span&gt;&lt;span&gt; base &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nullptr;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t size &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  size_t used &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * Set up an allocator. The allocator&amp;#39;s memory is guaranteed to be zeroed.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param a A pointer to an allocator to set up; cannot be `NULL`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param size The number of bytes to allocate. Must be greater than 0.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param addr The beginning address to allocate from. Pass `NULL` to defer base
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *             address selection to the operating system. Note that any other
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *             value greatly reduces portability.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @return `false` if setting up the allocator failed for any reason; `true`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *         otherwise.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;allocator_init&lt;&#x2F;span&gt;&lt;span&gt;(Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, size_t &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;size&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;addr&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * Allocate memory from the allocator.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param a The allocator to allocate from; cannot be `NULL`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param size The number of bytes to allocate. Must be greater than 0.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @return If successful, returns a pointer to the beginning of the allocated
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *         region. If unsuccessful for whatever reason, returns `NULL`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;allocator_alloc&lt;&#x2F;span&gt;&lt;span&gt;(Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, size_t &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;size&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * Reset an allocator, meaning that its memory pool is considered entirely
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * unused and the next attempt to allocate will start at the beginning of the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * backing memory pool. This does not deallocate anything from the operating
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * system&amp;#39;s perspective, but anything previously allocated in the allocator&amp;#39;s
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * memory pool should be considered invalid after calling this.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param a The allocator to reset; cannot be `NULL`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;allocator_reset&lt;&#x2F;span&gt;&lt;span&gt;(Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * Destroy an allocator.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @param a The allocator to destroy; cannot be `NULL`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * @return `false` if destroying the allocator failed for any reason.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;allocator_destroy&lt;&#x2F;span&gt;&lt;span&gt;(Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I give the user the option to specify the base address of the memory pool or
let the operating system pick it. In production, I&#x27;ll let the operating system
pick it, but in development, I&#x27;ll control the base address so I can predict the
general location of things in memory across program runs.&lt;&#x2F;p&gt;
&lt;p&gt;In the implementation of &lt;code&gt;allocator_init&lt;&#x2F;code&gt;, I use &lt;code&gt;mmap&lt;&#x2F;code&gt; instead of &lt;code&gt;malloc&lt;&#x2F;code&gt;,
like so:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;allocator_init&lt;&#x2F;span&gt;&lt;span&gt;(Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, size_t &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;size&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;addr&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span&gt;(a);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span&gt;(size);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; flags &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; MAP_PRIVATE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; MAP_ANONYMOUS;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(addr) {
&lt;&#x2F;span&gt;&lt;span&gt;    flags &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;|=&lt;&#x2F;span&gt;&lt;span&gt; MAP_FIXED_NOREPLACE;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  a-&amp;gt;base &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;mmap&lt;&#x2F;span&gt;&lt;span&gt;(addr, size, PROT_READ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; PROT_WRITE, flags, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;a-&amp;gt;base) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  a-&amp;gt;size &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; size;
&lt;&#x2F;span&gt;&lt;span&gt;  a-&amp;gt;used &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In short, we permit reading and writing on the allocated space, the memory
region is private to our process (&lt;code&gt;MAP_PRIVATE&lt;&#x2F;code&gt;), we do not use file-backed
memory (&lt;code&gt;MAP_ANONYMOUS&lt;&#x2F;code&gt; and &lt;code&gt;-1&lt;&#x2F;code&gt; for the file descriptor parameter), and
should the caller request a specific base address, we tell the operating
system to use a fixed address and force failure if our requested address has
already been mapped by us elsewhere in our process (&lt;code&gt;MAP_FIXED_NOREPLACE&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m using this as a frame allocator, meaning that at the beginning of each frame
(at the beginning of each iteration through the game loop), I call
&lt;code&gt;allocator_reset&lt;&#x2F;code&gt;. I set up the allocator right after initializing SDL near the
top of &lt;code&gt;main&lt;&#x2F;code&gt;, outside of the game loop. This allows me to use the allocator
before entering the game loop for any one-off scratch work I need to do, and
then immediately upon entering the game loop (with the exception of capturing
the frame start timestamp), the allocator is immediately reset.&lt;&#x2F;p&gt;
&lt;p&gt;Before entering the game loop, I use my relative-path-to-absolute-path utility
to build the full path to the game logic library so that I can check its last
modification time via &lt;code&gt;stat&lt;&#x2F;code&gt;. I then periodically re-build the full path in the
game loop when I go to re-check the modification time, so in the future, I will
likely also introduce separate, persistent storage to cache such things.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of having to implement my own way to get the base path of the game
binary across operating systems (using convenience functions in Windows and
checking &lt;code&gt;&#x2F;proc&lt;&#x2F;code&gt; things in Linux, for example), I can thankfully just leverage
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_GetBasePath&quot;&gt;SDL_GetBasePath&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The interface of my routine to build absolute paths looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;char* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;abs_path&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;const char* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;, Allocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It takes an allocator to use to obtain storage for the final string. The Jai
language I mentioned in a previous post does something similar for routines that
need to dynamically allocate but passes the current allocator to the function
implicitly in an argument called the context. I don&#x27;t have that language feature
in C&#x2F;C++ without approximating via OOP, globals, or function&#x2F;method partials,
so the allocator is an explicit parameter in my case. I don&#x27;t like this
signature much, but it accomplishes what I need. I haven&#x27;t covered this detail
yet, but I have also separated more of the game logic code from the &quot;platform&quot;
and exposed a higher-level &lt;code&gt;game_update&lt;&#x2F;code&gt; function from the game logic library
to the platform. The &lt;code&gt;game_update&lt;&#x2F;code&gt; function will likely eventually receive a
handle to the frame allocator so that it can allocate whatever it needs in a
single frame in a manner I can easily control and introspect. That is how
game logic code will also be able to use things like &lt;code&gt;abs_path&lt;&#x2F;code&gt; without owning
any particular allocator or backing memory pool.&lt;&#x2F;p&gt;
&lt;p&gt;Here are a couple other things I learned along the way. First, it is not only
Linux that has &lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;alloca.3.html&quot;&gt;alloca&lt;&#x2F;a&gt;;
Windows
&lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;c-runtime-library&#x2F;reference&#x2F;alloca?view=msvc-170&quot;&gt;has it&lt;&#x2F;a&gt;
as well. The main difference besides the literal function name is that Linux&#x27;s
causes undefined behavior on stack overflow whereas Windows&#x27;s will raise a
&lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;cpp&#x2F;structured-exception-handling-c-cpp?view=msvc-170&quot;&gt;structured exception&lt;&#x2F;a&gt;.
I could use this for building strings as I don&#x27;t anticipate memory required
for paths to be so large that it would cause a problem on the stack. The
second thing I learned is that man pages can even reference books. I noticed
the man page for &lt;code&gt;mmap(2)&lt;&#x2F;code&gt; (Linux man-pages 6.7, 2023-10-31) referenced
what appeared to be a book, and upon looking it up, indeed, it is &quot;POSIX. 4:
Programming for the Real World,&quot; by Gallmeister. Interesting, might want to
skim through it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-188</title>
        <published>2025-07-07T15:55:33+00:00</published>
        <updated>2025-07-07T15:55:33+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-188/"/>
        <id>https://lucidvisions.net/blog/2025-188/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-188/">&lt;p&gt;In my game, I want to control the pool of memory from which all allocations are
made. This will help with debugging and also general stability, if I can
allocate all memory I will need up front and then parcel that memory out
internally on-demand. I haven&#x27;t gone into much detail about why I am not using
a game engine, but having more control over allocations or at least the memory
pool being used is one of them.&lt;&#x2F;p&gt;
&lt;p&gt;SDL provides
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_SetMemoryFunctions&quot;&gt;SDL_SetMemoryFunctions&lt;&#x2F;a&gt;
which a developer can use to specify the functions to call in place of the
typical &lt;code&gt;malloc&lt;&#x2F;code&gt;, &lt;code&gt;calloc&lt;&#x2F;code&gt;, &lt;code&gt;realloc&lt;&#x2F;code&gt;, and &lt;code&gt;free&lt;&#x2F;code&gt;. I have previously
implemented my own simple linear allocator, from which all I ever did was
&quot;&lt;code&gt;malloc&lt;&#x2F;code&gt;&quot; a few times. SDL is its own animal, though, and until I read its
code, I need to treat it as if it could ({de|re})allocate at any time. This is
unfortunate since it complicates the problem for me, but it&#x27;s one price I pay
for using a powerful abstraction like SDL.&lt;&#x2F;p&gt;
&lt;p&gt;Although I am compiling SDL3 from source for my game, I have not read into the
code yet. To get a rough sense for how many allocations are happening and which
memory-related functions are actually called, I wrote shims for the memory
functions, registered them, and ran my game. One such shim looks like&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;static int&lt;&#x2F;span&gt;&lt;span&gt; my_malloc_count &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;my_malloc&lt;&#x2F;span&gt;&lt;span&gt;(size_t &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;my_malloc called; c: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0997b3;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, my_malloc_count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;malloc&lt;&#x2F;span&gt;&lt;span&gt;(s);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What I see is that a lot of allocation happens initially, both with &lt;code&gt;calloc&lt;&#x2F;code&gt; and
&lt;code&gt;malloc&lt;&#x2F;code&gt;. This is SDL initializing its subsystems. On shutdown, a lot of
&lt;code&gt;free&lt;&#x2F;code&gt;ing happens, which is cleanup. On the first pass through the game loop,
&lt;code&gt;calloc&lt;&#x2F;code&gt; is called hundreds of times, &lt;code&gt;realloc&lt;&#x2F;code&gt; is called maybe about ten times,
and &lt;code&gt;malloc&lt;&#x2F;code&gt; is called a few times. &lt;code&gt;realloc&lt;&#x2F;code&gt; is also called, although extremely
rarely relative to the other calls. Presumably SDL lazy-initializes some things,
and the first pass through the game loop is having SDL finish its initialization
as I use various subsystems. On all other iterations through the game loop, I
see only the following (keep in mind my game loop is relatively sparse right
now).&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;my_calloc called: c: 1160
&lt;&#x2F;span&gt;&lt;span&gt;my_free called; c: 251
&lt;&#x2F;span&gt;&lt;span&gt;my_free called; c: 252
&lt;&#x2F;span&gt;&lt;span&gt;my_malloc called; c: 297
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Debugging shows that
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_RenderPresent&quot;&gt;SDL_RenderPresent&lt;&#x2F;a&gt; is what I
call that in turn calls other functions which are responsible for the
(de)allocations above. For what it&#x27;s worth, I am using the GL rendering backend.
I switched to the software rendering backend and experience the same behavior.&lt;&#x2F;p&gt;
&lt;p&gt;In a previous life, the linear allocator I mentioned didn&#x27;t need to support
any efficient &lt;code&gt;free&lt;&#x2F;code&gt;ing because I only ever allocated a fixed number of times to
set up buffers I would later reuse. What I see here is that SDL will routinely
allocate and free. So should I want to point the memory allocators at my custom
memory pool, I need to design the pool and the functions that use it in a way
that will efficiently recycle memory.&lt;&#x2F;p&gt;
&lt;p&gt;By the way, I was initially calling &lt;code&gt;SDL_Log&lt;&#x2F;code&gt; in my allocator shims for
debugging, but this was a bad idea as &lt;code&gt;SDL_Quit&lt;&#x2F;code&gt; also uses the memory
functions after it has turned at least some subsystems off, causing SDL
to request re-initialization of the logging subsystem and then hang indefinitely
in what appears to be a sleep-and-check loop.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;my_free called
&lt;&#x2F;span&gt;&lt;span&gt;^C
&lt;&#x2F;span&gt;&lt;span&gt;Program received signal SIGINT, Interrupt.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(gdb) where
&lt;&#x2F;span&gt;&lt;span&gt;#0 in __GI___clock_nanosleep
&lt;&#x2F;span&gt;&lt;span&gt;#1 in __GI___nanosleep
&lt;&#x2F;span&gt;&lt;span&gt;#2 in SDL_SYS_DelayNS
&lt;&#x2F;span&gt;&lt;span&gt;#3 in SDL_Delay_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#4 in SDL_ShouldInit_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#5 in SDL_InitLog
&lt;&#x2F;span&gt;&lt;span&gt;#6 in SDL_CheckInitLog
&lt;&#x2F;span&gt;&lt;span&gt;#7 in SDL_GetLogPriority_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#8 in SDL_LogMessageV_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#9 in SDL_Log
&lt;&#x2F;span&gt;&lt;span&gt;#10 in my_free
&lt;&#x2F;span&gt;&lt;span&gt;#11 in SDL_free_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#12 in SDL_RemoveHintCallback_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#13 in SDL_QuitLog
&lt;&#x2F;span&gt;&lt;span&gt;#14 in SDL_Quit_REAL
&lt;&#x2F;span&gt;&lt;span&gt;#15 in SDL_Quit
&lt;&#x2F;span&gt;&lt;span&gt;#16 in main
&lt;&#x2F;span&gt;&lt;span&gt;(gdb) quit
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also noticed
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_aligned_alloc&quot;&gt;SDL_aligned_alloc&lt;&#x2F;a&gt; in the
SDL documentation. It has a corresponding &lt;code&gt;SDL_aligned_free&lt;&#x2F;code&gt; function as well.
The documentation says that &lt;code&gt;SDL_aligned_free&lt;&#x2F;code&gt; must be used to free memory
allocated with &lt;code&gt;SDL_aligned_alloc&lt;&#x2F;code&gt;, so I&#x27;m not concerned about having to
accommodate these (de)allocations in my implementation. But their presence does
raise the question of whether they would ever be called in my runtime. If they
are called, my hopes of centralizing and controlling memory allocation are
dashed. &lt;code&gt;SDL3&#x2F;SDL_Surface.h&lt;&#x2F;code&gt; defines the surface flag &lt;code&gt;SDL_SURFACE_SIMD_ALIGNED&lt;&#x2F;code&gt;
with the documentation&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Surface uses pixel memory allocated with SDL_aligned_alloc()&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;and &lt;code&gt;grep&lt;&#x2F;code&gt; suggests the aligned (de)allocator is generally only used when
leveraging
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Single_instruction,_multiple_data&quot;&gt;SIMD&lt;&#x2F;a&gt;. I also
see the aligned memory functions used in implementations for the PS2, PSP, and
PS Vita, but these are not targets of mine. I checked whether that surface flag
was set on surfaces I am loading, and it is not. So I can probably safely
ignore the aligned (de)alloc interface.&lt;&#x2F;p&gt;
&lt;p&gt;So now the question is, how much work is it going to be to implement my own
memory pool and allocator to support SDL&#x27;s behavior? Regardless of the
implementation effort, there is also the testing effort, such as unit tests
and then fuzzing with
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;sanitizers&#x2F;wiki&#x2F;addresssanitizer&quot;&gt;asan&lt;&#x2F;a&gt; enabled
or something like that.&lt;&#x2F;p&gt;
&lt;p&gt;...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.kernel.org&#x2F;doc&#x2F;gorman&#x2F;html&#x2F;understand&#x2F;understand011.html&quot;&gt;This&lt;&#x2F;a&gt;
explanation of slab allocators in older verions of Linux tells me I&#x27;m going
to be spending a lot of time writing an efficient allocator. The slab allocator
is an interesting concept and an appropriate one to use in my case, in my
opinion. One feature is that it caches previous allocations of particular
sizes, anticipating that such allocations may be requested again. This is
exactly the case in my game loop. It also does more advanced things such as
adding &quot;coloring&quot; (padding) between slabs to try and keep different allocated
regions in different cache lines, so that re-allocating one region does not evict
neighboring regions from the cache.&lt;&#x2F;p&gt;
&lt;p&gt;Then there is the backing
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Buddy_memory_allocation&quot;&gt;buddy allocator&lt;&#x2F;a&gt; which
handles coarser-grained allocations, typically on the order of pages. I don&#x27;t
need to bother much with this, as for my own pool I&#x27;d make a single &lt;code&gt;mmap&lt;&#x2F;code&gt; or
&lt;code&gt;malloc&lt;&#x2F;code&gt; call to the system to get the memory for my pool.&lt;&#x2F;p&gt;
&lt;p&gt;I would like to leverage something like an
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Region-based_memory_management&quot;&gt;arena allocator&lt;&#x2F;a&gt;
for per-frame storage and just clear the storage at the top of each iteration
of the game loop, but that &lt;code&gt;SDL_RenderPresent&lt;&#x2F;code&gt; calls &lt;code&gt;malloc&lt;&#x2F;code&gt; and doesn&#x27;t
seem to free that allocation until the next call through tells me I shouldn&#x27;t
free the underlying memory on a frame boundary. The Jai language has a dedicated
temporary storage that is recommended to be used in just this way for
applications with something like a game loop in them. (Aside: Jai is my favorite
language in the C++-contender space. I am just using my favorite dialect of C++,
which is C with funtion and operator overloading, for early game development
work because I want to reduce the amount of things I need to get better at all
at once.) I&#x27;d even accept two memory pools, one persistent across the lifetime
of the application for things like SDL subsystems, and the other being that
frame-temporary storage. Alas, I don&#x27;t think I can find the lifetime boundaries
of allocations made by dependencies so easily and thus don&#x27;t know when an
arena could be reset.&lt;&#x2F;p&gt;
&lt;p&gt;So I wondered, could I use something like
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jemalloc&#x2F;jemalloc&quot;&gt;jemalloc&lt;&#x2F;a&gt; and just hand over a memory
pool to the library and have it allocate from there? The answer is yes, although
this is non-trivial, and it of course brings in another sophisticated
dependency. So at the expense of making my game harder to trace and debug, I
would have more control over the memory (de)allocations. I&#x27;m not ready to make
that trade.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, I am toying with the idea of just letting SDL use the system-provided
allocators and introducing a simple bump or arena allocator for per-frame
storage. The current conclusion of my investigation is to put this off to a
later time. The control&#x2F;complexity tradeoff is not in my favor yet.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-187</title>
        <published>2025-07-06T22:14:10+00:00</published>
        <updated>2025-07-06T22:14:10+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-187/"/>
        <id>https://lucidvisions.net/blog/2025-187/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-187/">&lt;p&gt;I implemented basic code hot reloading. Say in your game, you are in the middle
of playtesting some long, complicated level, and you find some undesirable
behavior. You want to modify your game code to fix the behavior, but then you
have to recompile your game and get back to where you found the issue in order
to test your change--that or hack up a minimal reproducible example for this one
case--to verify if your fix worked.&lt;&#x2F;p&gt;
&lt;p&gt;Another approach is to separate the code for the game logic from the platform
code and then dynamically load the game logic into the running platform. This
way, you can change the game logic while keeping the game with all of its game
state alive and test changes much more quickly. I have to thank
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Ee3EtYb8d1o&quot;&gt;Handmade Hero&lt;&#x2F;a&gt; for this idea. I&#x27;m
currently developing on Ubuntu, so this post will cover how I achieved hot
reloading on Linux.&lt;&#x2F;p&gt;
&lt;p&gt;First, a simple demonstration. The video below shows a running game instance
where I have moved the player somewhere away from its origin. The tile the
player currently occupies is initially red, but then I change the game logic to
render it as green, recompile the game logic library, and the game instance
detects the library change, reloads the library, and then the tile is rendered
as green, all while keeping the game and its state alive.&lt;&#x2F;p&gt;
&lt;video controls preload=&quot;metadata&quot;&gt;
    &lt;source src=&quot;&#x2F;blog&#x2F;2025-187&#x2F;2025-07-06-22-06-hot-reloading.webm&quot; type=&quot;video&#x2F;webm&quot;&#x2F;&gt;
    Video not supported.
&lt;&#x2F;video&gt;
&lt;p&gt;Until now I kept all the game code in a single file, but I pulled some (rather
arbitrary) code out into a separate file to have something to compile into the
game logic library while I was standing up hot reloading. Once we do this, we
need to add a layer of abstraction between the functions the game logic library
provides and the callsites outside the library. Essentially, we replace direct
calls into some code with calls through function pointers that we point to
implementations of routines we want to call. The implementations exist somewhere
in our game logic library which we will now be dynamically loading into our
platform.&lt;&#x2F;p&gt;
&lt;p&gt;The gist is like this.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;lt;dlfcn.h&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;constexpr int64_t TARGET_FPS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;constexpr int64_t GAME_LIB_CHECK_EVERY_N_FRAMES &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; TARGET_FPS; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; Every second.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;typedef int &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;FooFn)(Foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;, Bar);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; stat lib_stat &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;const char*&lt;&#x2F;span&gt;&lt;span&gt; lib_pathname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;.&#x2F;path&#x2F;to&#x2F;lib.so&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; stat_ret &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;stat&lt;&#x2F;span&gt;&lt;span&gt;(lib_pathname, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;lib_stat);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span&gt;(stat_ret &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;uint64_t lib_stat_frame_counter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void*&lt;&#x2F;span&gt;&lt;span&gt; lib_handle &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;dlopen&lt;&#x2F;span&gt;&lt;span&gt;(lib_pathname, RTLD_LAZY);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span&gt;(lib_handle);
&lt;&#x2F;span&gt;&lt;span&gt;FooFn foo_fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(FooFn)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;dlsym&lt;&#x2F;span&gt;&lt;span&gt;(lib_handle, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;foo&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span&gt;(foo_fn);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span&gt;(run_game_loop) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; Maybe reload game logic library.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Foo f{};
&lt;&#x2F;span&gt;&lt;span&gt;  Bar b{};
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;foo_fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;f, b);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;typedef&lt;&#x2F;code&gt; establishes an interface between the library to hot reload and
the end user. Unlike what I show here, I&#x27;d put it in header files corresponding
to the library to be reloaded. We then deal with
&lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man2&#x2F;stat.2.html&quot;&gt;stat(2)&lt;&#x2F;a&gt; and
&lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;stat.3type.html&quot;&gt;stat(3)&lt;&#x2F;a&gt; which we will
be using to get at the file modification timestamp on our game logic library.
&lt;code&gt;stat&lt;&#x2F;code&gt; needs to get at &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Inode&quot;&gt;inode&lt;&#x2F;a&gt; information
and is thus a syscall, so it is &quot;slow,&quot; but we also aren&#x27;t going to be recompiling
our library every frame, so we don&#x27;t need to poll for the modification timestamp
on every iteration through the game loop. That&#x27;s where &lt;code&gt;lib_stat_frame_counter&lt;&#x2F;code&gt;
comes in--we&#x27;ll only poll every so many frames; in this case, once a second.&lt;&#x2F;p&gt;
&lt;p&gt;We next call &lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;dlopen.3.html&quot;&gt;dlopen&lt;&#x2F;a&gt; which
loads a library and gives us back a handle to it. We perform lazy symbol loading
with &lt;code&gt;RTLD_LAZY&lt;&#x2F;code&gt; to avoid loading in all symbols our library might contain when
I will only use a few of them on the platform side. To load symbols of interest,
we call &lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;dlsym.3.html&quot;&gt;dlsym&lt;&#x2F;a&gt;. We cast its
return value to the type of the interface our requested symbol represents.&lt;&#x2F;p&gt;
&lt;p&gt;Not shown in the code above, but in the game loop, every
&lt;code&gt;GAME_LIB_CHECK_EVERY_N_FRAMES&lt;&#x2F;code&gt;, we call &lt;code&gt;stat&lt;&#x2F;code&gt; again and check if the
modification timestamp on our library has changed. If it has, we
&lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;dlclose.3p.html&quot;&gt;dlclose&lt;&#x2F;a&gt; our current
handle on our game logic library and then repeat the &lt;code&gt;dlopen&lt;&#x2F;code&gt; and &lt;code&gt;dlsym&lt;&#x2F;code&gt; work
to get at the new implementation of our exported routines.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, the game loop calls our routines through our function pointers; shown
above is a call through &lt;code&gt;foo_fn&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In a C++ context, the symbol name of the function &lt;code&gt;foo&lt;&#x2F;code&gt; will be
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Name_mangling&quot;&gt;mangled&lt;&#x2F;a&gt;. To prevent this, we wrap
our declaration of &lt;code&gt;foo&lt;&#x2F;code&gt; in our game logic side inside &lt;code&gt;extern &quot;C&quot; { }&lt;&#x2F;code&gt;. If you
wanted to see symbol names in your library on Linux, you can do so with &lt;code&gt;nm&lt;&#x2F;code&gt;.
Here are actual (truncated) examples from the test code I hoisted into my game
logic library, first mangled and then not.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;$ nm -D build&#x2F;Debug&#x2F;libgame.so 
&lt;&#x2F;span&gt;&lt;span&gt;000000000000120f T _Z17map_pixel_to_tileP7Tilemapm8Vector2f
&lt;&#x2F;span&gt;&lt;span&gt;000000000000130e T _Z24player_render_tile_underP6PlayerP7TilemapP12SDL_Renderer
&lt;&#x2F;span&gt;&lt;span&gt;00000000000011b9 T _Z5round8Vector2f
&lt;&#x2F;span&gt;&lt;span&gt;$ nm -D build&#x2F;Debug&#x2F;libgame.so 
&lt;&#x2F;span&gt;&lt;span&gt;000000000000120f T map_pixel_to_tile
&lt;&#x2F;span&gt;&lt;span&gt;000000000000130e T player_render_tile_under
&lt;&#x2F;span&gt;&lt;span&gt;00000000000011b9 T _Z5round8Vector2f
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I don&#x27;t need &lt;code&gt;round&lt;&#x2F;code&gt; on the platform side, so I leave its symbol name mangled.
In this test, I do want &lt;code&gt;map_pixel_to_tile&lt;&#x2F;code&gt; and &lt;code&gt;player_render_tile_under&lt;&#x2F;code&gt;,
though, so I needed to not mangle their names.&lt;&#x2F;p&gt;
&lt;p&gt;Code hot-reloading is for development and debugging only. Release builds will
link the game library in statically.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-186</title>
        <published>2025-07-05T14:42:57+00:00</published>
        <updated>2025-07-05T14:42:57+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-186/"/>
        <id>https://lucidvisions.net/blog/2025-186/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-186/">&lt;p&gt;I implemented simple collision with the environment. This time, it is probably
clearer for me to show it in action first.&lt;&#x2F;p&gt;
&lt;video controls preload=&quot;metadata&quot;&gt;
    &lt;source src=&quot;&#x2F;blog&#x2F;2025-186&#x2F;2025-07-05-13-14-player-tile-tracking-collision.webm&quot; type=&quot;video&#x2F;webm&quot;&#x2F;&gt;
    Video not supported.
&lt;&#x2F;video&gt;
&lt;p&gt;There are a few new things going on in this video. The first is that the map
looks different from &lt;a href=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-185&#x2F;&quot;&gt;last time&lt;&#x2F;a&gt;. I updated the
tilemap to add a few things the player can collide with besides the walls along
the edges. I am currently exclusively using Aseprite for tilesets and tilemaps.
Unlike map editors such as &lt;a href=&quot;https:&#x2F;&#x2F;www.mapeditor.org&#x2F;&quot;&gt;Tiled&lt;&#x2F;a&gt;, Aseprite does
not provide a way to annotate tiles in a tilemap with auxiliary information. In
Tiled, you can do so, and one such attribute is drawing invisible bounding boxes
which can be used to indicate collision areas.&lt;&#x2F;p&gt;
&lt;p&gt;The way I annotated collision in Aseprite was to use an additional tilemap
layer. The tileset currently has only 2 tiles, technically 1 tile and the empty
tile which occupies an index in the tileset. I set the collision layer to be
higher in the layer stack than the map itself, set the opacity of the collision
layer to somewhere between 25 and 50 percent, and then I drew over any tile I
wanted to add collision to. I used a pink and black checkered pattern for the
tile indicating a collision area in hopes that it would stand out from other
tiles I may draw on the map.&lt;&#x2F;p&gt;
&lt;video controls preload=&quot;metadata&quot;&gt;
    &lt;source src=&quot;&#x2F;blog&#x2F;2025-186&#x2F;2025-07-05-15-09-50-aseprite.webm&quot; type=&quot;video&#x2F;webm&quot;&#x2F;&gt;
    Video not supported.
&lt;&#x2F;video&gt;
&lt;p&gt;This approach is not convenient because I need to manage at least 2 tilemaps
for every level now. It&#x27;s bug prone because I might change the underlying map
without updating the collision layer or vice versa, or I may change the index
values of tiles in the collision map but forget to do so in the game code. It
also forces a particular
software design on my game, having to parse a unique collision map for every
tilemap I load. With all of that said, it&#x27;s simple, and it works, so I&#x27;m going
with it for now.&lt;&#x2F;p&gt;
&lt;p&gt;I could design an alternative tilemap representation toward my game, as I do
&lt;a href=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-181&#x2F;&quot;&gt;control&lt;&#x2F;a&gt; the binary format of the exported tilemap. One
alternative is to set one of the high bits in the tile IDs in the map that
logically have collision, essentially merging my collision map into a bitfield
in each corresponding tile in the game map. If I recall correctly, Tiled does
something similar with bit-flag attributes on tiles. At this point in time,
it&#x27;s not worth me implementing this myself, though.&lt;&#x2F;p&gt;
&lt;p&gt;The ultimate goal is an in-game level editor, but I&#x27;m far from that at this
time.&lt;&#x2F;p&gt;
&lt;p&gt;The next thing in the video is the red square that seems to be following the
player. This is a visual annotation of the tile the player is considered to
occupy. I&#x27;ve added it for debugging. Unlike other kinds of software development,
it&#x27;s not so straightforward or even realistic to write traditional, automated
unit tests for implementations of various things in games. Often times we get more
information more quickly from visual feedback. Testing this way could also
explain how so many bugs slip through QA in games all the time.&lt;&#x2F;p&gt;
&lt;p&gt;Computing the tile the player occupies introduces us to what will become a
handful of coordinate systems in the game. Currently, for ease of development
and debugging, I have only one coordinate system in the game. (0, 0) is at
the upper-left corner of the game window, and it is the origin for both the
camera and the map. Furthermore, everything is measured in pixels, until now.
Determining the tile coordinate from a pixel coordinate, assuming the pixel
and tile coordinates share the same origin, is straightforward. In my case,
positions support sub-pixel accuracy and so are floating-point numbers.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span&gt;Vector2d &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span&gt;(Vector2f &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;v&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(int32_t)(v.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(int32_t)(v.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Vector2f pixel_xy &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ... ;
&lt;&#x2F;span&gt;&lt;span&gt;Vector2d int_pos &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span&gt;(pixel_xy);
&lt;&#x2F;span&gt;&lt;span&gt;size_t tile_pos_x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; int_pos.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; TILE_WIDTH_PX;
&lt;&#x2F;span&gt;&lt;span&gt;size_t tile_pos_y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; int_pos.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; TILE_HEIGHT_PX;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I designed the collision map and game map so that they are the same dimensions
and so that the tile width and height are the same across maps. I can therefore
compute the index into the collision map tiles and check whether a tile has
collision area or not. If the player tries to move into a map tile with backing
collision area, I disallow the position update. That&#x27;s what we see with me
running into walls in the first video.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we color the tile the player is occupying, we can also see that
although the player is currently rendered as an entire tile, the player position
is represented as a single point, the upper-leftmost point of the player tile.
Because of this, the player can somewhat walk into walls to their right and
below them. I&#x27;ll address this later on by changing the position of the player
relative to the player&#x27;s rendered tile or perhaps changing the player&#x27;s
collision point to a collision area.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, although it is difficult to tell, we stick to walls somewhat. For
example, if I press against a wall on my left side and then attempt to move
diagonally up-left or down-left, I won&#x27;t move at all. This is because my
proposed position is up-left or down-left of me which enters the collision
area on my left, and my entire position update is disallowed. I&#x27;ll fix this
eventually, likely by checking whether we can move horizontally separately
from whether we can move vertically.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-185</title>
        <published>2025-07-04T20:25:29+00:00</published>
        <updated>2025-07-04T20:25:29+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-185/"/>
        <id>https://lucidvisions.net/blog/2025-185/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-185/">&lt;p&gt;I implemented basic player movement in the game. First, I created a simple
tileset for the player and loaded that into the game as a single SDL texture.
Each tile in the tileset is 16x16 pixels, and there are 4 tiles. The tiles are
blue squares with a white arrow showing which direction the player is facing, so
I have one tile per cardinal direction.&lt;&#x2F;p&gt;
&lt;p&gt;I then created a simple &lt;code&gt;Player&lt;&#x2F;code&gt; struct to hold a pointer to the texture and
other supporting information such as the cardinal direction the player is facing
and the player&#x27;s position.&lt;&#x2F;p&gt;
&lt;p&gt;On each iteration through the game loop, I poll for and handle keyboard events.
In the event handling logic, I save off the new key states into an instance of
my own &lt;code&gt;Buttons&lt;&#x2F;code&gt; struct. I don&#x27;t modify the player state immediately in the
event-handling code. The simple &lt;code&gt;Buttons&lt;&#x2F;code&gt; struct looks as follows.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Button {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt; pressed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Buttons {
&lt;&#x2F;span&gt;&lt;span&gt;  Button up &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;  Button down &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;  Button left &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;  Button right &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For now, we just keep track of whether the button is down on the current frame.
We don&#x27;t track whether it was pressed this frame or has been held down for some
number of frames. With this setup, if we wanted to understand the input delta
from the last frame, we could simply create a second instance of &lt;code&gt;Buttons&lt;&#x2F;code&gt; and
store the latest frame&#x27;s inputs in the second instance at the end of the game
loop so that it is available on the next iteration through the loop.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span&gt;Buttons previous_buttons &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span&gt;(running) {
&lt;&#x2F;span&gt;&lt;span&gt;  Buttons buttons &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;SDL_PollEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;event)) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;switch &lt;&#x2F;span&gt;&lt;span&gt;(event.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;case&lt;&#x2F;span&gt;&lt;span&gt; SDL_EVENT_KEY_UP:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; Fall through.
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;case&lt;&#x2F;span&gt;&lt;span&gt; SDL_EVENT_KEY_DOWN: {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(event.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; SDLK_DOWN) {
&lt;&#x2F;span&gt;&lt;span&gt;            buttons.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;down&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;pressed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; event.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; SDL_EVENT_KEY_DOWN;
&lt;&#x2F;span&gt;&lt;span&gt;          } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;          }
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;  previous_buttons &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; buttons;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When it comes time to update the player based on input, I just check whether a
certain button is pressed and then update the player&#x27;s facing direction and a
proposed position.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span&gt;Vector2f proposed_position &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; player.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;position&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt; dpos &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(TARGET_FRAME_TIME_NS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1000000000.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*
&lt;&#x2F;span&gt;&lt;span&gt;             RUNNING_VELOCITY_MS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; METERS_PER_PIXEL;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(buttons.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;down&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;pressed&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  player.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;facing &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Direction:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;DOWN;
&lt;&#x2F;span&gt;&lt;span&gt;  proposed_position.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;+=&lt;&#x2F;span&gt;&lt;span&gt; dpos;
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(buttons.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;up&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;pressed&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  player.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;facing &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Direction:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;UP;
&lt;&#x2F;span&gt;&lt;span&gt;  proposed_position.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;-=&lt;&#x2F;span&gt;&lt;span&gt; dpos;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; The the same for right, left.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;player.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;position &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; proposed_position;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here I assume that I always hit my target frame time, and then I multiply that
time converted to seconds with my configured running velocity to get a distance
the player would have moved in meters. The screen is drawn in pixels, though, so
I convert meters to pixels, and that is my delta position (&lt;code&gt;dpos&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;I also handle down-up and right-left separately. This way the player can move
both vertically and horizontally at the same time. I handle down before up,
meaning that if the player presses both down and up at the same time, down takes
priority. They will face and move downward. (Notice that I add, not subtract,
&lt;code&gt;dpos&lt;&#x2F;code&gt; in the down case--SDL&#x27;s coordinate system places (0, 0) at the top-left
of the rendering space.)&lt;&#x2F;p&gt;
&lt;p&gt;Handling right-left separately means that the player will always prioritize
facing a right-left cardinal direction instead of a down-up one if either right
or left were pressed on a frame.&lt;&#x2F;p&gt;
&lt;p&gt;Although I simply update the player&#x27;s position with the proposed position here,
I am setting myself up for handling collision. It&#x27;s possible we should not be
able to move where we want to move, so I keep the proposed position separate so
I can test it before updating the player state.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a video of what I have so far. The grey border is supposed to be walls,
and the white is just a placeholder tile for the ground. You can see the blue
player moving around with the arrow updating in the direction the player is
facing (or the prioritized direction if the player is moving diagonally). The
frame rate is low, but you can tell by the diagonal movement that while the map
is based on tiles, the movement is based on pixel positions and is not
tile-based. Two outstanding issues are the fact that there is no collision yet
and that the player is faster if moving diagonally as opposed to just vertically
or horizontally.&lt;&#x2F;p&gt;
&lt;video controls preload=&quot;metadata&quot;&gt;
    &lt;source src=&quot;&#x2F;blog&#x2F;2025-185&#x2F;2025-07-02-basic-movement.webm&quot; type=&quot;video&#x2F;webm&quot;&#x2F;&gt;
    Video not supported.
&lt;&#x2F;video&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-184</title>
        <published>2025-07-03T15:40:29+00:00</published>
        <updated>2025-07-03T15:40:29+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-184/"/>
        <id>https://lucidvisions.net/blog/2025-184/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-184/">&lt;p&gt;When I went to implement basic player movement, I needed to capture keyboard
input. This is done in SDL by polling for events and then processing
keyboard-related ones.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span&gt;SDL_Event e;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;SDL_PollEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;e)) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;switch &lt;&#x2F;span&gt;&lt;span&gt;(e.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;case&lt;&#x2F;span&gt;&lt;span&gt; SDL_EVENT_KEY_UP:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; Fall through.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;case&lt;&#x2F;span&gt;&lt;span&gt; SDL_EVENT_KEY_DOWN: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(event.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; SDLK_DOWN) {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What I want to focus on in this post is the implementation of &lt;code&gt;SDL_Event&lt;&#x2F;code&gt;. If
you look at its &lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_Event&quot;&gt;documentation&lt;&#x2F;a&gt;, you&#x27;ll
see that it&#x27;s a &lt;code&gt;union&lt;&#x2F;code&gt;. Its first member is &lt;code&gt;type&lt;&#x2F;code&gt; which encodes the event
type--it&#x27;s what we&#x27;re switching on in the example above. If you look at the
implementation of any of the &lt;code&gt;SDL_*&lt;&#x2F;code&gt; union members, you will see the first
field of each of those structs is an instance of &lt;code&gt;SDL_EventType&lt;&#x2F;code&gt;.
&lt;code&gt;SDL_EventType&lt;&#x2F;code&gt; is implemented as a C enum, which is represented as an &lt;code&gt;int&lt;&#x2F;code&gt;
behind the scenes, which is the same number of bits (although different
semantics) as the &lt;code&gt;Uint32&lt;&#x2F;code&gt; (&lt;code&gt;uint32_t&lt;&#x2F;code&gt;-equivalent) &lt;code&gt;type&lt;&#x2F;code&gt; member in the
&lt;code&gt;SDL_Event&lt;&#x2F;code&gt; union on nearly all modern platforms.&lt;&#x2F;p&gt;
&lt;p&gt;For reference, see the implementations of, for example,
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_ClipboardEvent&quot;&gt;SDL_ClipboardEvent&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_TouchFingerEvent&quot;&gt;SDL_TouchFingerEvent&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_MouseButtonEvent&quot;&gt;SDL_MouseButtonEvent&lt;&#x2F;a&gt;,
or &lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_QuitEvent&quot;&gt;SDL_QuitEvent&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, no matter which member of the union you work with, the first
field will always be something encoding the event type.&lt;&#x2F;p&gt;
&lt;p&gt;This construct is called a
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Tagged_union&quot;&gt;discriminated union&lt;&#x2F;a&gt; (or tagged
union). Here is an all-in-one example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span&gt;TypeTag { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;kFoo&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;kBar&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;kNone &lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Foo {
&lt;&#x2F;span&gt;&lt;span&gt;  TypeTag tag;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Bar {
&lt;&#x2F;span&gt;&lt;span&gt;  TypeTag tag;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;union &lt;&#x2F;span&gt;&lt;span&gt;DiscriminatedUnion {
&lt;&#x2F;span&gt;&lt;span&gt;  TypeTag tag;
&lt;&#x2F;span&gt;&lt;span&gt;  Foo foo;
&lt;&#x2F;span&gt;&lt;span&gt;  Bar bar;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With discriminated unions, you can avoid inheritance and still compose one type
that behaves as an instance of this or that type depending on some context, and
you just check the value of &lt;code&gt;tag&lt;&#x2F;code&gt; to know how to access fields of the union. You
lose anything you might have otherwise gained through polymorphism, and the
total size of &lt;code&gt;DiscriminatedUnion&lt;&#x2F;code&gt; is the size of its largest member (plus
padding, potentially; see the interesting note at the bottom of the &lt;code&gt;SDL_Event&lt;&#x2F;code&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_Event#syntax&quot;&gt;implementation&lt;&#x2F;a&gt;). However, the
discriminated union construct supports
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Data-oriented_design&quot;&gt;data-oriented design&lt;&#x2F;a&gt; by
concentrating data of interest nearby in memory instead of potentially scattered
about through a network of pointers (whether these pointers be
pointers-to-base-class in some data structure, or members of instances pointing
to this and that data, or the CPU figuring out which methods to call via
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dynamic_dispatch&quot;&gt;dynamic dispatch&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-183</title>
        <published>2025-07-02T10:17:27+00:00</published>
        <updated>2025-07-02T10:17:27+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-183/"/>
        <id>https://lucidvisions.net/blog/2025-183/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-183/">&lt;p&gt;Today a simple feature I added to the game was a target frame rate. This is
typically done in one of three ways.&lt;&#x2F;p&gt;
&lt;p&gt;The first is by using timers in your game
code. You capture the start time of when processing begins for a single frame,
then you capture the time processing ends for that frame. Subtract the times,
and if the time required to prepare the frame is less than your target frame
time, sleep the thread for the difference in times.&lt;&#x2F;p&gt;
&lt;p&gt;The major issue with this approach is that the thread you&#x27;re collecting timing
information on can be preempted at any time by the operating system. Depending
on which operating system (or language library) features you use to measure
time, you may accidentally be ignoring the time your thread spent off the CPU.
That time passes in the world outside the computer and so can cause
inconsistencies between the time your game thinks elapsed and the wall-clock
time that actually elapsed.&lt;&#x2F;p&gt;
&lt;p&gt;The second way to achieve a target frame time is by using performance counters.
Performance counters are typically built into modern processors and track some
kind of low-level hardware event. In this way, the counter itself is independent
of operating system nuances such as scheduling. The operating system provides an
API to access the performance counter value. As we don&#x27;t necessarily know &lt;em&gt;what&lt;&#x2F;em&gt;
is being measured by the performance counter, we don&#x27;t know what the count is
relative to. Thus, the counter value itself is not a measure of the passage of
time. The operating system also provides an API to query the frequency of
increments to the performance counter, and with this information, we can compute
the passage of time.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;dt_s = (perf_counter_now - perf_counter_then) &#x2F; perf_counter_frequency
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;SDL3 provides
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_GetPerformanceCounter&quot;&gt;SDL_GetPerformanceCounter&lt;&#x2F;a&gt;
and
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_GetPerformanceFrequency&quot;&gt;SDL_GetPerformanceFrequency&lt;&#x2F;a&gt;
for this purpose.&lt;&#x2F;p&gt;
&lt;p&gt;The third way is to externalize synchronization, to rely on display hardware to
indicate when a new frame can be drawn. This is done via &quot;vertical
synchronization,&quot; also called
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Screen_tearing#Vertical_synchronization&quot;&gt;VSync&lt;&#x2F;a&gt;.
If your GPU supports VSync and you enable VSync in your renderer, your GPU
synchronizes drawing the frame buffer with the refresh rate of your display
hardware (ex: your monitor). If your mornitor supports running at 60 Hz and 144
Hz, and if you configure your monitor to run at 144 Hz, then your GPU will send
whatever is in your graphics buffer to the display 144 times per second, making
your display frame rate 144 Hz.&lt;&#x2F;p&gt;
&lt;p&gt;To use VSync effectively in your game, you likely need to make your game loop
frame rate-independent. If your player has a very fast refresh rate on their
monitor but your game sometimes takes a while to draw its next frame, you might
not hit your frame timing. It&#x27;s no problem to draw the same thing twice while
you&#x27;re preparing your next frame, but you need to make sure you account for the
passage of time independent of refresh rate (performance counters help with
this). It helps to do general math in your game in SI units--meters, seconds,
etc. This way regardless of whether you hit your frame time or not, the movement
of things in your game (for example) is natural. Things don&#x27;t move faster than
expected on fast refresh rates and slower than expected on slower refresh rates.&lt;&#x2F;p&gt;
&lt;p&gt;SDL3 provides
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_SetRenderVSync&quot;&gt;SDL_SetRenderVsync&lt;&#x2F;a&gt; for
this purpose.&lt;&#x2F;p&gt;
&lt;p&gt;Regarding the first and second approaches to hitting a target frame rate, they
are complicated by two assumptions.&lt;&#x2F;p&gt;
&lt;p&gt;The first assumption is that your player&#x27;s
display hardware is some harmonic of your desired target frame time. In other
words, if your target frame rate is 60 FPS, then if your player&#x27;s monitor is
rendering at 60 FPS or perhaps 120 FPS, you most likely will not encounter
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Screen_tearing&quot;&gt;screen tearing&lt;&#x2F;a&gt;. Your 60 Hz game
loop might be out of phase of the display hardware&#x27;s refresh cycle, but this
means you&#x27;ll always have your next frame prepared by the time the display
hardware draws its next image. Also, your game&#x27;s cycle time is not outpacing or
falling behind the cycle time of the display hardware, so you will likely never
be partway through preparing a new frame when the display hardware starts
reading the frame data to draw the next image. If your cycle timing is not a
harmonic of the display hardware&#x27;s, your game loop will, theoretically,
eventually be updating a frame while the display hardware is reading the frame
buffer to draw its next image.&lt;&#x2F;p&gt;
&lt;p&gt;The second assumption is that you always hit your frame time. Even if your
cycle time is a harmonic of that of your display hardware, if your game loop
runs longer than your target frame time, you are at risk of screen tearing.&lt;&#x2F;p&gt;
&lt;p&gt;With all this said, unfortunately none of these three techniques guarantees
smooth rendering. See this great
&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@alen.ladavac&#x2F;the-elusive-frame-timing-168f899aec92&quot;&gt;article&lt;&#x2F;a&gt;
for why.&lt;&#x2F;p&gt;
&lt;p&gt;As for me, for now, I am using the performance counter technique for simplicity.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-182</title>
        <published>2025-07-01T16:34:01+00:00</published>
        <updated>2025-07-01T16:34:01+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-182/"/>
        <id>https://lucidvisions.net/blog/2025-182/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-182/">&lt;p&gt;Today I implemented simple tilemap rendering. In my
&lt;a href=&quot;https:&#x2F;&#x2F;lucidvisions.net&#x2F;blog&#x2F;2025-181&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;, I discussed exporting a tilemap from an
Aseprite file as a binary file. I parsed that binary file in my game and then
used &lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3_image&#x2F;FrontPage&quot;&gt;SDL3_image&lt;&#x2F;a&gt; to load the
tileset image. I was considering using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nothings&#x2F;stb&quot;&gt;stb&lt;&#x2F;a&gt;
image to do the loading, but since I&#x27;m committing to SDL, I figured I&#x27;d learn
SDL3_image. If it feels too heavy, I&#x27;ll switch to stb.&lt;&#x2F;p&gt;
&lt;p&gt;After building and using SDL3_image, when launching my game, the main window
would open, close quickly after, and then open again. The fix for this was
calling &lt;code&gt;SDL_CreateWindow&lt;&#x2F;code&gt; with the &lt;code&gt;SDL_WINDOW_HIDDEN&lt;&#x2F;code&gt; flag and then showing
the window only after I create the renderer (which I do separately from creating
the window).&lt;&#x2F;p&gt;
&lt;p&gt;The gist of accelerated graphics rendering in SDL is to load your media into
a &lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_Surface&quot;&gt;surface&lt;&#x2F;a&gt; and then from a surface
create a &lt;a href=&quot;https:&#x2F;&#x2F;wiki.libsdl.org&#x2F;SDL3&#x2F;SDL_Texture&quot;&gt;texture&lt;&#x2F;a&gt;. In my case, I am
dealing with tilemaps which themselves are built from tilesets. For the
uninitiated, I&#x27;ll give a quick outline.&lt;&#x2F;p&gt;
&lt;p&gt;A tileset consists of sprites. A sprite is essentially an image. Using text as
make-believe images, an example tileset might contain these three &quot;sprites&quot;:
&lt;code&gt;_.=&lt;&#x2F;code&gt;. Using the previous three characters, we might build the map:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;========
&lt;&#x2F;span&gt;&lt;span&gt;=_..___=
&lt;&#x2F;span&gt;&lt;span&gt;=___...=
&lt;&#x2F;span&gt;&lt;span&gt;========
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Who knows what that represents, but it is a map built from the &quot;sprites&quot; in our
tileset. If we were talking about actual computer graphics, our map would be
some larger, rendered image. The rendered image&#x27;s size in RAM would increase as
the image got larger, but we know that the image (in this examlpe) is only
composed of three unique sprites, those from our tileset. We could compress the
tilemap by instead representing it as indices into the tileset.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;22222222
&lt;&#x2F;span&gt;&lt;span&gt;20110002
&lt;&#x2F;span&gt;&lt;span&gt;20001112
&lt;&#x2F;span&gt;&lt;span&gt;22222222
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The sprites in a tileset are also known as tiles. So here we have represented a
tilemap by showing which tiles to use when rendering the map by referring to
tiles (by index) in a tileset.&lt;&#x2F;p&gt;
&lt;p&gt;Back to SDL, this leaves me with multiple ways to actually store the tile data.
Currently my binary tilemap export only encodes a single tilemap (due to the way
I drew the map in Aseprite). I could load that tileset and then parse individual
tiles out of its pixel memory, create a surface for each unique tile, and then
create a texture for each unique surface. Nothing wrong with this. But what I
did instead was create a single texture for the tileset, and at render time, I
index into the &lt;em&gt;texture&lt;&#x2F;em&gt; to render only a portion of the tileset texture (the
tile I want) to the window. The upside of this is that there are fewer resources
to deal with, fewer allocations. The downside is that all tiles are stored in a
single texture, so I cannot apply per-tile modulation (recoloring, transforming,
etc.) as easily.&lt;&#x2F;p&gt;
&lt;p&gt;Here is an excerpt of code I wrote showing how I render from this single tileset
texture.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * Render a tilemap instance to a renderer. `tileset` is expected to contain
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * the entire tileset referred to by the tilemap, and the tile properties are
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; * assumed to be compatible with dimensions specified in the tilemap.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;tilemap_render&lt;&#x2F;span&gt;&lt;span&gt;(Tilemap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;tm&lt;&#x2F;span&gt;&lt;span&gt;, SDL_Texture&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;tileset&lt;&#x2F;span&gt;&lt;span&gt;, SDL_Renderer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;renderer&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  uint16_t tileset_tile_width &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tileset-&amp;gt;w &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;tile_width_px;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;height_tiles; i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; j &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; j &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;width_in_tiles; j&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      uint16_t tile_index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;tiles[i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;width_in_tiles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; j];
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt; tile_x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tile_index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt; tileset_tile_width &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;tile_width_px;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt; tile_y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tile_index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; tileset_tile_width &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;tile_height_px;
&lt;&#x2F;span&gt;&lt;span&gt;      SDL_FRect src_rect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        tile_x, tile_y,
&lt;&#x2F;span&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;)tm-&amp;gt;tile_width_px, (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;)tm-&amp;gt;tile_height_px
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;      SDL_FRect dst_rect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;)j &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;tile_width_px,
&lt;&#x2F;span&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;)i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; tm-&amp;gt;tile_height_px,
&lt;&#x2F;span&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;)tm-&amp;gt;tile_width_px,
&lt;&#x2F;span&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;)tm-&amp;gt;tile_height_px
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;SDL_RenderTexture&lt;&#x2F;span&gt;&lt;span&gt;(renderer, tileset, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;src_rect, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;dst_rect);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I haven&#x27;t done much work on the general encapsulation; just standing things up
right now.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-181</title>
        <published>2025-06-30T19:28:11+00:00</published>
        <updated>2025-06-30T19:28:11+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-181/"/>
        <id>https://lucidvisions.net/blog/2025-181/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-181/">&lt;p&gt;Today marks my first day working full-time on my own projects. I&#x27;m focusing on
building a game. Toward learning how everything works end-to-end from first
principles, I&#x27;m building a game engine. I will likely ship with SDL3, though.&lt;&#x2F;p&gt;
&lt;p&gt;My game will feature tilemaps built from pixel art tiles. I love using
&lt;a href=&quot;https:&#x2F;&#x2F;www.aseprite.org&#x2F;&quot;&gt;Aseprite&lt;&#x2F;a&gt; for my art. At the time of this writing,
Aseprite supports building tilemaps but only supports exporting them using
an out-of-tool &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dacap&#x2F;export-aseprite-file&quot;&gt;script&lt;&#x2F;a&gt;. This
script exports tilemaps and supporting data as JSON. I don&#x27;t want to write or
depend on a JSON parser, though, so I chose to fork and extend the script to
output binary.&lt;&#x2F;p&gt;
&lt;p&gt;Aseprite scripts are written in Lua, so I needed to learn Lua. I half-jokingly
searched for &quot;learn lua in 10 minutes&quot; and turned up
&lt;a href=&quot;https:&#x2F;&#x2F;tylerneylon.com&#x2F;a&#x2F;learn-lua&#x2F;&quot;&gt;Learn Lua in 15 Minutes&lt;&#x2F;a&gt;. Using this, the
&lt;a href=&quot;https:&#x2F;&#x2F;www.lua.org&#x2F;docs.html&quot;&gt;official documentation&lt;&#x2F;a&gt;, and asking an LLM
questions here and there, I picked up Lua pretty quickly.&lt;&#x2F;p&gt;
&lt;p&gt;Some interesting things I learned are that Lua has a way to allow one Lua file
to import another (&lt;code&gt;require&lt;&#x2F;code&gt;) which caches anything that the imported file runs
on import so that when additional imports of the same file happen, nothing
top-level runs again. There is another mode (&lt;code&gt;dofile&lt;&#x2F;code&gt;) which imports without
caching, always running anything exposed at the top-level scope in the imported
file. Another thing is that Lua only has a table data structure which doubles
as an array. Array indices start from &lt;code&gt;1&lt;&#x2F;code&gt;, not &lt;code&gt;0&lt;&#x2F;code&gt;. You can use negative
indicies when accessing the command-line arguments to find arguments passed to
Lua itself, not to your script.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, without showing the full glue code, I introduced this &lt;code&gt;binary.lua&lt;&#x2F;code&gt; to
the exporter script, added command-line argument parsing to the main script,
and allowed the caller to either export JSON or binary. I&#x27;ll stick with binary
and write a simple parser in my game.&lt;&#x2F;p&gt;
&lt;p&gt;When calling a non-graphical script from Aseprite, you do so from the command
line. You pass args before specifying the script. This is for at least Aseprite
v1.3.14.3.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#fafafa;color:#383a42;&quot;&gt;&lt;code&gt;&lt;span&gt;aseprite -b foo.aseprite --script-param bar=baz --script my_script.lua
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The content of &lt;code&gt;binary.lua&lt;&#x2F;code&gt; in its initial form is below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; style=&quot;background-color:#fafafa;color:#383a42;&quot; class=&quot;language-lua &quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Export an Aseprite tilemap and supporting data to a binary file.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Output Schema
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- -------------
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- schema_major_ver: uint8_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- schema_minor_ver: uint8_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- schema_patch_ver: uint8_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- canvas_width_px : uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- canvas_height_px: uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- tilesets        : array&amp;lt;tileset&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- layers          : array&amp;lt;layer&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- array
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- -----
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- An array `array&amp;lt;T&amp;gt;` is a single `uint64_t` indicating the number of instances
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- of `T` immediately after. The instances of `T` are contiguous in memory.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- For example, a 2-element array of type `array&amp;lt;uint8_t&amp;gt;` is laid out as
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- follows in memory:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--   uint64_t (num elements)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--   uint8_t  (first element)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--   uint8_t  (second element)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- string
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- ------
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- A string is a single `uint64_t` indicating the number of characters
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- immediately following and then said number of characters. The characters are
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- 8-bit bytes. Each byte is a single ASCII character.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- tileset
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- -------
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- image_pathname: string
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- tile_width_px : uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- tile_height_px: uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- layer
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- -----
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- name        : string
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- tileset_id  : uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- width_tiles : uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- height_tiles: uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- tiles       : array&amp;lt;index_into_tileset&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- index_into_tileset
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- ------------------
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- index: uint16_t
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;--
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Notes:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- - Strings do not support Unicode.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- - All numbers are encoded as little-endian.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- - The current schema only supports a single cel per layer.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;binary &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;_version_major &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;_version_minor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;_version_patch &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Write select keys out of table `t` to the open file `f` as binary. See
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- the output schema in the file documentation for what will be written out to
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- `f`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;binary&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;encode&lt;&#x2F;span&gt;&lt;span&gt;(f, t)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Schema version.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;binary&lt;&#x2F;span&gt;&lt;span&gt;._version_major))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;binary&lt;&#x2F;span&gt;&lt;span&gt;._version_minor))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;binary&lt;&#x2F;span&gt;&lt;span&gt;._version_patch))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Canvas width, height.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.width))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.height))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Tilesets.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.tilesets))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.tilesets &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;ts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.tilesets[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span&gt;.image))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span&gt;.image)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span&gt;.grid.tileSize.width))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span&gt;.grid.tileSize.height))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a0a1a7;&quot;&gt;-- Layers.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.layers))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.layers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;.layers[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.name))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.name)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.tileset))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;if #&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.cels &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;Layer &amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;.. &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;.. &lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot; has more than 1 cel&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;cel &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.cels[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;cel&lt;&#x2F;span&gt;&lt;span&gt;.tilemap.width))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;cel&lt;&#x2F;span&gt;&lt;span&gt;.tilemap.height))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;cel&lt;&#x2F;span&gt;&lt;span&gt;.tilemap.tiles))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;j &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c18401;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;cel&lt;&#x2F;span&gt;&lt;span&gt;.tilemap.tiles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(string.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#0184bc;&quot;&gt;pack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#50a14f;&quot;&gt;&amp;quot;&amp;lt;I2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;cel&lt;&#x2F;span&gt;&lt;span&gt;.tilemap.tiles[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;j&lt;&#x2F;span&gt;&lt;span&gt;]))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a626a4;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e45649;&quot;&gt;binary
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-175</title>
        <published>2025-06-24T19:10:11+00:00</published>
        <updated>2025-06-24T19:10:11+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-175/"/>
        <id>https://lucidvisions.net/blog/2025-175/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-175/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;cFIlta1GkiE?t=74&quot;&gt;1:14&lt;&#x2F;a&gt; to 1:28.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-172</title>
        <published>2025-06-21T23:11:25+00:00</published>
        <updated>2025-06-21T23:11:25+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-172/"/>
        <id>https://lucidvisions.net/blog/2025-172/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-172/">&lt;p&gt;A friend shared &lt;a href=&quot;https:&#x2F;&#x2F;danluu.com&#x2F;look-stupid&#x2F;&quot;&gt;this article&lt;&#x2F;a&gt; with me recently,
talking about how worrying about looking stupid often just stunts your growth. I agree!
People should see how &lt;em&gt;intentionally&lt;&#x2F;em&gt; stupid ML models are while they&#x27;re &quot;growing,&quot;
until they&#x27;re suddenly smarter than Sapiens.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025-171</title>
        <published>2025-06-20T22:19:44+00:00</published>
        <updated>2025-06-20T22:19:44+00:00</updated>
        
        <author>
          <name>
            
              orphen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucidvisions.net/blog/2025-171/"/>
        <id>https://lucidvisions.net/blog/2025-171/</id>
        
        <content type="html" xml:base="https://lucidvisions.net/blog/2025-171/">&lt;p&gt;My friend sent me &lt;a href=&quot;https:&#x2F;&#x2F;futurism.com&#x2F;chatgpt-mental-health-crises&quot;&gt;this article&lt;&#x2F;a&gt;
recently about people spiraling into delusions aided by conversations with ChatGPT.&lt;&#x2F;p&gt;
&lt;p&gt;For some reason, the phenomenon of declining birthrates in first-world countries
quickly came to mind. There are many explanations for declining birthrates, but that it
is mostly only happening in first-world countries seems like a well-veiled form of
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Wireheading&quot;&gt;wireheading&lt;&#x2F;a&gt;. We used to need kids to
guarantee we have hands to work the farms and sustain the empire. Then we had kids to
carry on the family lineage. Then we had kids because, who is going to take care of me
when I&#x27;m older? Then we had kids just because that&#x27;s what grown-ups do, right? Now having
kids is increasingly relegated to reacting to biological urges and personally-held images
of family. The more well-off a country becomes, the less any one individual needs to be
responsible for anything critical on the community level, and things that were once
critical to survival now largely come for &quot;free,&quot; and we feel some sort of existential
emptiness from not having a larger-than-self, meaningful goal to grind away towards. We
often fill this emptiness with both readily-available pleasures and
ultimately-meaningless, artificially-difficult quests. We&#x27;re just wireheading ourselves.&lt;&#x2F;p&gt;
&lt;p&gt;The models of ChatGPT alluded to in the article presumably did not have an explicit goal
to radicalize or delude anyone. The models&#x27; ability to assume arbitrary human-like
personas had an un(?)intended side-effect of finding the
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Swiss_cheese_model&quot;&gt;holes in the swiss cheese&lt;&#x2F;a&gt; of the
constructs that help keep one comfortably integrated into society, the constructs that
sit in front of the users&#x27; psyches. Side-stepping the question of whether ChatGPT is
&quot;good&quot; or &quot;bad&quot; in this regard, it seems to me that the delusional in the article are
&lt;em&gt;liking&lt;&#x2F;em&gt; the whole experience.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
