<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <title>NULLPOINTER</title>
    <link href="https://tomnullpointer.github.io/gh-pages/feed.xml" rel="self" />
    <link href="https://tomnullpointer.github.io/gh-pages" />
    <updated>2025-12-02T15:36:28+00:00</updated>
    <author>
        <name>me</name>
    </author>
    <id>https://tomnullpointer.github.io/gh-pages</id>

    <entry>
        <title>ProcJam 25 Diary - p3 bells and whistles</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p3-bells-and-whistles.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p3-bells-and-whistles.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-10_14_58-.png" medium="image" />
            <category term="Procedural Generation"/>
            <category term="Game Development"/>
            <category term="Explainer"/>

        <updated>2025-11-28T09:47:03+00:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-10_14_58-.png" alt="" />
                    This is the final article about my spooky SCP bunker generator that&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-10_14_58-.png" class="type:primaryImage" alt="" /></p>
                <figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_20_43--2.png" alt="" width="1902" height="975" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--2-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--2-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--2-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--2-xl.png 1024w"></figure>
<p>This is the final article about my spooky SCP bunker generator that I revised for ProcJam 25. You can read the previous parts <a href="https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p1-dungeon-gen.html" target="_blank" rel="noopener noreferrer">here </a>and <a href="https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p2-solids.html" target="_blank" rel="noopener noreferrer">here</a>. You can also play the final demo <a href="https://tomnullpointer.itch.io/labx19" target="_blank" rel="noopener noreferrer">here</a>.</p>
<p>Today I'm going to talk about how I take the plain 3d room generator and add props and atmospheric elements to add some of that juicy Stalker SCP vibe.</p>
<h2>Preparing to decorate</h2>
<p>Like all good DIY projects preparation is important. I want to place furniture and other items in my newly generated rooms, but there needs to be some logic in how they are instantiated. Having chairs facing walls or ladders leaning against nothing might look odd (and not in the right way). </p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/walls.png" alt="" width="645" height="358" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/walls-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/walls-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/walls-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/walls-xl.png 1024w"></figure>
<p>To add some sense to the furniture placement I flag cells in the map with certain qualities. For example in the image above, cells on the periphery of a room are marked green and cells that are at a door entrance are flagged in red. I can use this to make sure that items aren't placed where they might block doors, and I can use the "wall adjacent" information to place and align objects like shelving or cupboards, items of furniture which would naturally fit next to a wall.</p>
<h2>Procedural Kitbashing</h2>
<p>Its probably important at this point to explain how I use/choose/edit the assets I use in this project (and most of my other work). I pride myself on a sort of procedural kitbashing approach.</p>
<p>I start with assets I have either bought or found free from various sources (Unity asset store, Fab store etc). I create a separate scene from the rest of my project where I set up these objects in order to prepare them for use. At this stage I often write new shaders to apply to all the objects, add layers of extra texturing to their materials, edit/add/remove elements to the fbxs using blender and frequently combine models form different sources to create a new object. I like to think of this as similar to the workshops in set design where artists will paint on grime or modify costumes in order to create a more holistic set of pieces. Of course lighting, colour grading and so on are also really good at bringing together disparate source materials.</p>
<p>When I have enough items prepared that I like the look of I set up a list of categories for them, something like the following.</p>
<blockquote>
<p> public GameObject[] m_CeilingObjects;<br> public GameObject[] m_WallLightOnObjects;<br> public GameObject[] m_WallLightOffObjects;<br> public GameObject[] m_RoofTopObjects;<br> public GameObject[] m_RoofLightObjects;<br> public GameObject[] m_FloorWallObjects;<br> public GameObject[] m_FloorWallFreeObjects;</p>
</blockquote>
<p>The locations where these items are placed should be obvious from their names, and I can use the map data I already have (see previous articles), in order to place them sensibly.</p>
<p>Here are a couple of examples of some placement rules.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_21_24-.png" alt="" width="1850" height="938" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_21_24--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_21_24--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_21_24--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_21_24--xl.png 1024w"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_20_43-.png" alt="" width="1902" height="975" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_20_43--xl.png 1024w"></figure>
<p>When instantiating these objects my code always attempts to leave 1 cell around any object (especially if it is freestanding) in order to ensure the player can navigate the surrounding space. But I don't want every room to have the same set of furniture or the same style of placement layout. So how can I avoid this?</p>
<h2>Room decoration types</h2>
<p>I use a set of pre-defined room "types" which hold specific model sets to create a theme for an individual room. Since all the rooms have individual ids and data its trivial to assign a type to each one in the data.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_25_31-Ruins_Bunker3-Microsoft-Visual-Studio.png" alt="" width="272" height="176" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_25_31-Ruins_Bunker3-Microsoft-Visual-Studio-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_25_31-Ruins_Bunker3-Microsoft-Visual-Studio-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_25_31-Ruins_Bunker3-Microsoft-Visual-Studio-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_25_31-Ruins_Bunker3-Microsoft-Visual-Studio-xl.png 1024w"></figure>
<p>These room types hold furniture lists (see above) and specific placement rules. Such as the school-room placing desking and chairs in organised rows. The rooftops are decorated in a similar way, as they are effectively just rooms without walls. </p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_28_14-.png" alt="" width="1870" height="960" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_28_14--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_28_14--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_28_14--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_28_14--xl.png 1024w"></figure>
<p>With this groundwork established.  its mostly about adding in extra furniture sets, different floor and wall materials and iterating on how these elements look when combined. The demo features around 6 or 6 room types, but feels like more because the rooms shapes and materials can also change.</p>
<p>Because I am generating everything in discreet blocks (see previous article) Its also easy for my code to occasionally omit objects, railings, bits of walls or even entire floors. This introduces even more variation and gives an nice sense of decay or deconstruction to the architecture.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_30_38-.png" alt="" width="1786" height="974" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_30_38--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_30_38--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_30_38--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_30_38--xl.png 1024w"></figure>
<h2>Connections</h2>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_31_38--2.png" alt="" width="1876" height="965" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_31_38--2-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_31_38--2-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_31_38--2-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_31_38--2-xl.png 1024w"></figure>
<p>The corridors from my underlying map generation data are either drawn as enclosed tunnels, or as exposed gantries. This gives another nice mix of material types and allows more sightlines and verticality to the scene.</p>
<p>Similarly, the connections between floors are built with ladder placements (the spiral staircase tiles from our original 2d generator!), and in some cases I even allow the floors themselves to use gantry tiles to create a more vertiginous vibe.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_45_23-.png" alt="" width="1884" height="969" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_45_23--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_45_23--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_45_23--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_45_23--xl.png 1024w"></figure>
<p>The surroundings of the generated base are supplied by a predefined terrain patch that I hand authored. I make sure to set a decent fog range so the player cant see the distant edges.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/2025-11-28-09_37_32-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="1027" height="884" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_37_32-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_37_32-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_37_32-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/33/responsive/2025-11-28-09_37_32-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<h2>Anomalies</h2>
<p>What's a containment facility without the anomalies!? There are about seven or so anomalies and they can appear in any room (but no more than one type per rooms). Some are subtle and static (placed like furniture), some wander around, some are interactive, and there's even a (fairly rare) poltergeist!</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/ezgif-2694e94e4c5bf215.gif" alt="" width="800" height="450"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/ezgif-2b4ba47c3ab37be9.gif" alt="" width="800" height="450"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/ezgif-279a9892811a8500.gif" alt="" width="800" height="450"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/33/ezgif-2232d50837db8897.gif" alt="" width="800" height="450"></figure>
<h2>Conclusions</h2>
<p>I make a lot of prototypes, and almost all of them end up resting in some dusty folder on my PC never to see the light of day. And although I didn't fully develop this demo during procjam25 I did fix it up significantly and spent a decent amount of time documenting the process. I'm glad I did, I think its a cool project and I'm very happy that I've returned to it and made it playable. You can download the demo on itch and let me know what you think there or on my socials.</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>ProcJam 25 Diary - p2 a 3D dungeon</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p2-solids.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p2-solids.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-23-17_06_41-.png" medium="image" />
            <category term="Procedural Generation"/>
            <category term="Game Development"/>
            <category term="Explainer"/>

        <updated>2025-11-24T17:35:46+00:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-23-17_06_41-.png" alt="" />
                    This is the second part of my 2025 procjam diary. The previous&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-23-17_06_41-.png" class="type:primaryImage" alt="" /></p>
                <figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-17_00_53-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-2.png" alt="" width="855" height="779" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_00_53-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-2-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_00_53-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-2-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_00_53-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-2-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_00_53-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-2-xl.png 1024w"></figure>
<p>This is the second part of my 2025 procjam diary. The previous entry is <a href="https://www.nullpointer.co.uk/procjam-25-diary-p1-dungeon-gen.html" target="_blank" rel="noopener noreferrer">here</a>.</p>
<p>The floor plan above doesn't look much different from where we left off yesterday. Apologies for all the lines and garish saturated colours (Its a screenshot from the game, where I actually float the map in the 3d scene space alongside the geometry to compare, hence why its a bit grungy). Anyway the main difference with this stage of the process is that I have done another pass across the grid to properly mark walls for rooms (cyan) and walls for corridors (blue)</p>
<p>I've also flagged some tiles on the room walls (blue again) to indicate extra holes I want to punch through the geometry. This is just because Id like to give the buildings a sort of decayed look.</p>
<h2>Tiles to Blocks</h2>
<p>The actual 3d construction of the world is done in a very "minecraft" kind of way. For most of the tiles in the map I simply place a 3d cube with the appropriate texture.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-17_13_28-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="1175" height="884" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_13_28-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_13_28-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_13_28-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_13_28-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<p>The image above shows the corridor area to the bottom right of the map (the one at the top of this post). I've removed the ceiling tiles from most of it to show how the interior looks. Because we are dealing with an FPS game corridors are 1 cube wide but their containing walls are 2 cubes high. Each map layer also has a sort of <em>Above </em>and <em>Below </em>set of cubes that is implicitly defined by the room or corridor and its total height. The corridor section above looks like this in First Person (obviously with the ceiling tiles back on )</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-17_10_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="1167" height="877" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_10_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_10_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_10_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_10_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<p>And here is an example of a simple room generated in the same way</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/Untitled-s1.png" alt="" width="1169" height="900" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/Untitled-s1-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/Untitled-s1-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/Untitled-s1-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/Untitled-s1-xl.png 1024w"></figure>
<p>I've masked out the rest of the scene in photoshop so its easier to see the details of the specific room we are looking at. You might be able to see that this room is on an upper floor (check the previous post to see how I'm generating the different floor maps). The room has various exits, some chunks missing from the walls and even a ladder leading down to a lower level (by the red light on the floor). The ladder takes the place of the  "spiral staircase" tiles in the 2d map generator pass. The light on the wall is part of a prop decoration phase that I will talk about later.</p>
<p>Hopefully you can see that the cubes aren't perfect cubes, in fact they are all a bit wonky. why?</p>
<h2>Sorry Plato</h2>
<p>People can sense pure geometric forms. Using them in games can make the environments feel synthetic and artificial. That's fine in loads of contexts, but not if you want to represent a decaying or organic world. So instead of using perfect cubes my system generates meshes for its building blocks and then subtly wobbles the edge/corner points to add imperfections and a create a more organic feel</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-17_28_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="541" height="539" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_28_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_28_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_28_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_28_44-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<p>This process is a little more complex than it might seem at first because you cant just add a wobble to every edge. If you do, there is a strong chance you would introduce weird discontinuities where a stack of blocks has gaps appear half way up it. A block <em>on the top</em> of a wall can have a wobbly <em>upper </em>surface, but not a wobbly <em>lower </em>edge. My code accounts for this by creating a bitmasking neighbourhood value for every cube in the 3d array. This is similar to how 2D tile-map sprite games often represent terrain types and calculate how walls should be drawn. There's a good explanation <a href="https://code.tutsplus.com/how-to-use-tile-bitmasking-to-auto-tile-your-level-layouts--cms-25673t" target="_blank" rel="noopener noreferrer">here</a>. </p>
<p>The cubes use a single counter to track the empty spaces around them in each of the 3d axes. For example a space to the north counts +1, south +2, east +4, west +8, up +16, down +32. The final number can be checked to see exactly where the cube should have solid faces, and where they connect to neighbouring cubes.. You end up with 64 possible meshes. My code builds all of these and then wobbles the edges and corners that it knows are ok to wobble (i.e. they don't form seams with neighbouring blocks). </p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-17_31_42-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="575" height="552" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_31_42-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_31_42-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_31_42-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_31_42-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<p>This process also gives us free back-face culling (as seen above), since we don't need to generate the faces that will not be seen.</p>
<p>My system actually makes several copies of the entire 64 cube set, each with different random wobbles. This might seem arduous, but it can all be done in a fairly simple code loop, and the resulting meshes are pulled into the 3d world wherever a block is needed. The final result behaves like a sort of wonky lego set. The 2d layered building map is therefore turned into a 3d array of cubes with wonky edges!</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-17_39_56-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="761" height="888" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_39_56-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_39_56-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_39_56-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-17_39_56-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<h2>AutoPainting</h2>
<p>Now as we have seen I will do everything I can in code to avoid having to do any modelling or scene arrangement. The same is true for texturing. The meshes generated in the phase explained above  have no UVs (texture coordinates) and no associated materials. They are simply meshes with verts and normals.</p>
<p>One advantage of using cubes (or near cubes) for the construction process is that I can apply textures to them based on their positions and angles in the world (which I know will always be rectinlinear). I do this using a series of tri-planar shaders I wrote specifically for the project.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/tutorial-image-1620326397.jpg" alt="" width="1024" height="512" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/tutorial-image-1620326397-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/tutorial-image-1620326397-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/tutorial-image-1620326397-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/tutorial-image-1620326397-xl.jpg 1024w"></figure>
<p>You will have seen tri-planar shaders before, there is an excellent tutorial on building them from <a href="https://catlikecoding.com/unity/tutorials/advanced-rendering/triplanar-mapping/" target="_blank" rel="noopener noreferrer">catlikecoding</a> (also the source of the image above). They basically use the normals of a mesh combined with its world position to apply a texture to it.</p>
<p>Its as if the texture is projected from a certain side of the world and will splat itself onto any surface that faces the projector. Since my cubes are always facing consistent directions I can rely on a triplanar projection to texture them in a seamless tiling way. As long as the repeating interval of the texture in my shader matches the world scale of a cube the mapping should always line up.</p>
<p>Now of course I wasn't happy with just a basic shader, so I made something that adds some variance to the texturing. The following  screenshot shows the material parameters I am using.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-18_10_50-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P.png" alt="" width="549" height="842" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_10_50-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_10_50-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_10_50-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_10_50-Ruins_Bunker3-testscene-new-PC-Mac-and-Linux-Standalone-Unity-2019.4.41f1-P-xl.png 1024w"></figure>
<p>Firstly we have 2 main textures, one that is applied to surfaces facing NSEW and another for surfaces facing UP or DOWN. These two textures also have metallic and normal counterparts (just like a normal PBR style shader). But I also have 2 other textures involved. A gradient map and a noise map.</p>
<p>The noise map is read in a similar way to the 2 axis maps I just mentioned, and creates a pseudo 3d noise sampler. The values from this noise map are then used to cross-mix the other textures with a sort of masking. I use this mostly on the floors to generate a more organic mix of materials. (Seen below in the mix of the diamond grill texture and the rebar-concrete texture)</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-18_42_11-.png" alt="" width="1548" height="866" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_42_11--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_42_11--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_42_11--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_42_11--xl.png 1024w"></figure>
<p>The gradient map has a slightly different use. It applies a colour tint to the blocks,  based on the world position of the block and a scaling value. Its similar to how sedimentary rock has different layers of colours stacked vertically in its structure.</p>
<p>This gradient lookup is also modified by the 3d noise which disrupts any obvious stripes and results in a subtle sediment style mottling effect on the walls.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/32/2025-11-24-18_43_58-.png" alt="" width="793" height="932" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_43_58--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_43_58--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_43_58--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/32/responsive/2025-11-24-18_43_58--xl.png 1024w"></figure>
<p>I'm pretty happy with the overall results which create a more realistic and organic sense of decaying architecture. The variations in mesh edges combined with the noise influenced shaders means that the repetition commonly seen in PCG structures is softened a bit.</p>
<p>In the next article I'm going to cover probably the last part of the construction process, adding props and anomalies to the scene.</p>
<p> </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>ProcJam 25 Diary - p1 dungeon gen</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p1-dungeon-gen.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/procjam-25-diary-p1-dungeon-gen.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-18_48_41-.png" medium="image" />
            <category term="Procedural Generation"/>
            <category term="Game Development"/>
            <category term="Explainer"/>

        <updated>2025-11-23T16:51:52+00:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-18_48_41-.png" alt="" />
                    Procjam Its ProcJam time again https://itch.io/jam/procjam and this year I decided that&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-18_48_41-.png" class="type:primaryImage" alt="" /></p>
                <figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-15_38_12-.png" alt="" width="1017" height="763" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-15_38_12--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-15_38_12--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-15_38_12--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-15_38_12--xl.png 1024w"></figure>
<h2>Procjam</h2>
<p>Its ProcJam time again <a href="https://itch.io/jam/procjam">https://itch.io/jam/procjam</a> and this year I decided that rather than spin up a new side-project that will end up unfinished in a dusty archive .. (see <a href="https://www.nullpointer.co.uk/procedurally-generating-books.html" target="_blank" rel="noopener noreferrer">here</a>) I would revamp something that I had worked on previously but never really made presentable.</p>
<p>So this is the first post in what I hope to be a short diary series about my benched spooky-liminal-horror SCP building generator. </p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-17_06_41-.png" alt="" width="1637" height="952" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_06_41--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_06_41--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_06_41--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_06_41--xl.png 1024w"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-17_07_48-.png" alt="" width="1903" height="940" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_07_48--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_07_48--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_07_48--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_07_48--xl.png 1024w"></figure>
<p>The final version is a randomly generated ruined bunker style lab, inspired by Stalker, SCP and the backrooms. I will explain a bunch of the systems that enabled the final 3D version in a later post. But for now I'm going to talk about the basis for this project, and many other PCG games. The dungeon generator! Its pictured in the header image here and you can play with the WebGL version over on itch at this link</p>
<p><a href="https://tomnullpointer.itch.io/multi-floor-dungeon-generator" target="_blank" rel="noopener noreferrer">https://tomnullpointer.itch.io/multi-floor-dungeon-generator</a></p>
<p>Although I am a very visual designer I still try to build the environmental structure of my games into an abstracted data form. This is partly because I'm lazy and I hate laying out endless props and wall pieces, but also because having your game world in an independent data structure makes it much easier to change elements of the world in a direct and manageable way. It also means that when you come to generating the 3d visuals you can skin the data in a variety of different ways without needing to change its data model counterpart.</p>
<p>To create the bunker I wanted to work with some form of dungeon room/corridor map. And as with another old project of mine <a href="https://www.nullpointer.co.uk/castle-generation-in-ruins.html" target="_blank" rel="noopener noreferrer">In Ruins</a> I wanted to sort of invert the construction, turning corridors into causeways and solid earth into air. I thought this would help to produce the more external industrial architecture you might find at an abandoned base. I realised that I could probably achieve this with a few tweaks to a standard dungeon generator system.</p>
<h2>Starting with Donjon d20</h2>
<p>I started by replicating most of the functions used in the <a href="https://donjon.bin.sh/d20/dungeon/" target="_blank" rel="noopener noreferrer">Donjon d20 generator</a>. This is a favourite of mine, partly because of its flexibility, but also because it tends to generate fairly compact spaces with a nice mix of room sized areas and mazelike corridors.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-17_19_29-Publii.png" alt="" width="656" height="837" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_19_29-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_19_29-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_19_29-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-17_19_29-Publii-xl.png 1024w"></figure>
<p>The original code is available on the site, so I ported most of the system into c# for unity. The basic generation algorithm is as follows</p>
<p>1. Define an empty map of x by y tiles</p>
<p>2. Try to place a room of a randomly chosen dimension (between min and max dimension) somewhere in the map</p>
<p>3. If its possible to place the room, add it and mark all the tiles that room encompasses with an id for the room. </p>
<p>4. Repeat from step 2 until you have enough rooms (or close enough)</p>
<p>5. Loop over all the placed rooms and mark the edge cells as walls, also mark some of those edge cells as potential exits.</p>
<p>6. Start to fill the map with corridors. (This part gets its own subsection)</p>
<ul>
<li>Start a corridor in an empty tile.</li>
<li>Dig out the corridor in a random direction and change direction if is blocked, or based on other stylistic factors (i.e. always change direction ever 4 cells to make a very windy tunnel)</li>
<li>If a tunnel can connect to an adjacent door way then connect it!</li>
<li>If a corridor cannot proceed any further in any direction then move to the next available empty tile and start a new corridor again from the first step.</li>
<li>Once enough corridors have been placed or enough doorways have been linked exit this stage. (again this can be a stylistic choice)</li>
</ul>
<h2>Pruning</h2>
<p>The approach described above will generally result in a lot of corridors! In many cases the entire map will end up packed full of rooms and corridors. This will connect most together, but can end up with a lot of useless extra corridors or dead ends. So now its time to prune!</p>
<p>Any room that has a door-exit which doesn't link to a corridor has that tile replaced with a wall tile. Then a recursive process checks every corridor tile. if the tile has only 1 other corridor as a neighbour it is a dead-end and the tile is removed. This process of removing dead-end corridor tiles is repeated, causing corridors to shrink back each pass until all the dead ends are removed. You can obviously leave some if you like the way they look!</p>
<h2>An Odd Trick</h2>
<p>The secret sauce of this entire approach is its adherence to what id call an odd/even placement rule. If we were to place rooms and corridors entirely randomly we would end up with corridors and rooms running parallel to each other in adjacent tiles. This would make some corridors feel like they were 2 tiles wide and causes all kinds of artefacts melding rooms with corridors etc.</p>
<p>So instead we ONLY use rooms of odd dimensions and we ONLY place doors and corridor spawns on odd tiles. As we dig corridors each movement step also needs to be of a length that will leave the current corridor at another odd grid co-ordinate. If we stick to this rule then all corridors are guaranteed to run with a single tile border of solid dungeon around them and doors will always be aligned to these corridor paths. I've added a few col/row numbers and a pink highlight below to show how this works.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/Untitled-1-3.png" alt="" width="712" height="763" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-1-3-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-1-3-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-1-3-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-1-3-xl.png 1024w"></figure>
<h2>Multi-floor</h2>
<p>Now I've used almost this exact algorithm before to generate dungeons or ruins. But I was interested to see how it might be extended into 3d. What if the dungeon rooms were able to exist over multiple floors? We might be able to have larger spaces with high ceilings that could allow players to enter a room on one floor but leave on another.</p>
<p>Making this addition turned out to be fairly trivial. Instead of a 2d array of tiles I made a 3d array, where each y layer is a separate floor of the dungeon. When generating the floor 0 I allow any successfully placed room to to have a y dimension (in addition to its existing width and depth) to represent the number of floors it extends upwards to. </p>
<p>Then each subsequent floor above won't generate new rooms until it has checked for existing rooms below that might "punch through" to its level. If a lower room does reach into the new layer it is placed just like a new one, but in the original location of its lower level source.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/Untitled-2.png" alt="" width="1290" height="650" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-2-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-2-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-2-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/Untitled-2-xl.png 1024w"></figure>
<p>The image above shows floor 0 and floor 1 of a random generation. Here Room A exists on both floors and in the second layer has a balcony around its edge, a spiral staircase (to allow players to move up and down) and a new corridor connecting it to the east. The rooms B and C also extend in the same way, except that on the second floor C has only a balcony and no onward corridors.</p>
<p>Ok so we now have a system that will generate the basic room and corridor information I might use for building this bunker</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/31/2025-11-23-19_15_19-.png" alt="" width="672" height="674" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-19_15_19--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-19_15_19--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-19_15_19--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/31/responsive/2025-11-23-19_15_19--xl.png 1024w"></figure>
<p>The image above shows the sort of map I had in mind for the bunker/lab idea. If I were to imagine the corridors as raised causeways or gantries and each room as a separate holding area I could start to generate the sort of space I had in mind. The fact that some of the corridor connections might seem a little nonsensical is actually fine in the fiction of a liminal SCP space since logical connections are often replaced with more surreal architecture.</p>
<p>Next time.. from 2d to 3d</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Should you be able to kill the quest giver? - RPG design</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/should-you-be-able-to-kill-the-king-rpg-design.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/should-you-be-able-to-kill-the-king-rpg-design.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/30/1-1527823267-2.jpg" medium="image" />
            <category term="Game Development"/>

        <updated>2025-11-16T10:13:04+00:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/30/1-1527823267-2.jpg" alt="" />
                    I have been prototyping a new game, an RPG of sorts. And&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/30/1-1527823267-2.jpg" class="type:primaryImage" alt="" /></p>
                <p>I have been prototyping a new game, an RPG of sorts. And while working on the behaviour of NPCs I realised I was going to have to make a decision regarding they question above.. Should players be able kill important quest givers or plot characters in their playthrough? and if so how should I deal with the consequences. I was interested in finding out what sort of sentiments and solutions already existed for this problem, so I made a quick bsky post to see what people thought.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/2025-11-16-09_48_47-Publii.png" alt="" width="601" height="205" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-09_48_47-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-09_48_47-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-09_48_47-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-09_48_47-Publii-xl.png 1024w"></figure>
<p><a href="https://bsky.app/profile/tomnullpointer.bsky.social/post/3m5o52cdej22j" target="_blank" rel="noopener noreferrer">https://bsky.app/profile/tomnullpointer.bsky.social/post/3m5o52cdej22j</a></p>
<p>I was happy to see that other folk were also interested in the problem and I ended up with almost 100 responses  with loads of interesting viewpoints and lots of solutions and examples. Rather than let those results vanish along with the thread I thought I might collate some of them here.</p>
<p>So the basic problem is as follows: Should a player be allowed to kill important quest-givers in an RPG, and if so how does the game deal with the consequences. </p>
<p>Many games don't attempt to be super-realistic when it comes to the simulation of NPCs.  A lot of RPG designs (almost all the final fantasy titles for instance) have a strict division between combat and exploration states, and you cant attack anything outside of those specific delineations. In most RPGs killing an NPC just isn't possible. </p>
<p>However games that lean more towards a simulationist approach might present scenarios where the player <em>could</em> harm or kill some (or all) of the NPCs they encounter. This might range from deliberately attacking a shopkeeper, to accidentally setting the king on fire with the edge of a misplaced AOE attack. Removing the ability to harm NPCs is valid solution but could reduce the players sense of agency and make the gameworld seem more artificial.</p>
<p>At this point I am going to assume that we want to at least entertain the idea that you can attack NPCs (rather than outright removing the possibility). So what are the different ways we could tackle the resulting situations.</p>
<p>I can't include all the replies I had to the original post (you can see them in the link above) But Ill try to summarise some of the main ones.</p>
<h2>No, that didn't happen</h2>
<p>This is the default route for most games, but it can be handled in different ways. The most basic approach is simply to make key NPCs invulnerable, or ban any combat actions in the zones where those NPCs exist, in sanctuary zones etc. This solution doesn't even pretend that the action in question is possible, which is fine, but definitely frames the game as a less sim/immersive experience. An alternative is to allow the drama but ignore the consequences.</p>
<p>In Oblivion, key NPCs wont die when they are reduced to 0 HP, instead they fall unconscious for a short time and then get back up a few moments later. This does add some sense of drama, since your companions can fall in battle and be unable to support you etc. But there is no permanent consequence, and dead folk standing back up in the middle of a battle can look a little silly.</p>
<h2>Let it happen but Lazarus your way out of the issue</h2>
<p>This is a common solution, by having a mechanism to resurrect dead key NPCs the game allows any quest lines to be continued. The death of the original NPC becomes a bump in chain rather than a real change of path. In many of the Souls games the NPCs can be killed and then 'resurrected' as ghost-like entities, so you can still shop or talk to them. There are a number of variations of this approach that folk outlined in the thread.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/2025-11-16-10_33_05-Publii.png" alt="" width="595" height="177" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_33_05-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_33_05-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_33_05-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_33_05-Publii-xl.png 1024w"></figure>
<p>I love this idea! Its got a weird almost valhalla type vibe to it where you could imagine  reincarnation is both a mechanic and part of in-world lore.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/2025-11-16-10_25_38-Publii.png" alt="" width="598" height="272" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_25_38-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_25_38-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_25_38-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_25_38-Publii-xl.png 1024w"></figure>
<p>I've only played the first few hours of New Vegas (I should go back) so I wasn't aware of the Yes Man. Its a compromise that limits the extent of quest-chain damage. Everyone is killable but one character - the Yes Man - will respawn. This immortal NPC can then facilitate missions and stop soft locks form happening. It avoids the world being full of ghosts but still allows a certain amount of murderous agency.</p>
<h2>Let it happen and don't say anything</h2>
<p>Some games, particularly older titles would allow the player to brick their game and not even admit this had happened. I don't think this is an acceptable approach in contemporary design. Having broken the quest line in hour one but not knowing until four hours later is quite disrespectful. It also leads to the same sort of issue you can find in 4x games, where you have lost the game but spend three hours as a dead man walking. I doubt many player would be happy with this outcome now (or even then!)</p>
<h2>Let it happen but inform the player</h2>
<p>As many posters pointed out, this is what happens in Morrowind.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/bafkreibvz4foh5epjc25c74rkywdon6sj6rv2ug6iraafj2ipt76rl563q.jpg" alt="" width="1000" height="309" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/bafkreibvz4foh5epjc25c74rkywdon6sj6rv2ug6iraafj2ipt76rl563q-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/bafkreibvz4foh5epjc25c74rkywdon6sj6rv2ug6iraafj2ipt76rl563q-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/bafkreibvz4foh5epjc25c74rkywdon6sj6rv2ug6iraafj2ipt76rl563q-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/bafkreibvz4foh5epjc25c74rkywdon6sj6rv2ug6iraafj2ipt76rl563q-xl.jpg 1024w"></figure>
<p>I think that this is a decent solution (and I love the wording of the warning here). But it's obviously a meta response that occurs outside of the game world and as a result is totally non-diegetic. its a similar approach to the familiar pop-up dialogues you get before entering end sequences "Once you open this door you will be unable to go back to the previous zones, are you sure?" It works, and arguably its still true to a simulationist world because there are consequences - you won't be able to finish the main quest. But it usually means that there aren't any alternative paths to resolve the problem should you choose to continue with your murder. </p>
<p>To be fair to morrowind, it does provide at least one alternative.</p>
<p>https://en.uesp.net/wiki/Morrowind:Yagrum_Bagarn_and_Wraithguard </p>
<h2>Let it happen and respond in world</h2>
<p>This approach remains true to the expectations of a real simulation, but is also the one that involves the most work. The resolution here is that any quest chains that can be disrupted must have alternative path that can be explored. In a simple example, killing a key holder might result in you looting the key from their corpse instead of being given it as a reward.</p>
<p>For this to work, all paths usually need some sort of un-block-able route to fall back on. As Cara points out, designers should be aware of how such fall back routes might lead to "less interesting" gameplay and they should either inform the player that they are going to be heading to a less rich solution, or they should make sure the alternatives are equally interesting.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/2025-11-16-10_46_39-Publii.png" alt="" width="587" height="360" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_46_39-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_46_39-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_46_39-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_46_39-Publii-xl.png 1024w"></figure>
<p>For example, if you can kill all key NPCs, then perhaps the game can fill your remaining time with interesting events that reflect the trauma you have caused to the gameworld.</p>
<p>I've not played Atomfall, but I know that the both this game and Weird West present alternative routes in this way. It was important to the designers of both these games to allow players such murderous agency but also provide alternative routes that were interesting to pursue. But why would you spend all that extra time and energy working out those alternatives when you could force the player along a guaranteed critical path instead.</p>
<h2>What does it feel like?</h2>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/2025-11-16-10_57_42-Publii.png" alt="" width="585" height="315" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_57_42-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_57_42-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_57_42-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_57_42-Publii-xl.png 1024w"></figure>
<p>One of the reasons Game of Thrones was so memorable is that the the key characters are not immortal. Indeed a significant number of them get killed in a single episode (you know the one).Its shocking and dramatic and Is one of the main elements that people reference when praising the series. But why is it so effective?</p>
<p>Most games present the world as a sideshow for the players rollercoaster ride, YOU are the focus, and everything around you is designed to serve the story that carries you through the game. Its a power fantasy, and nothing disrupts a power fantasy more than part of the set falling over when its supposed to support and entertain you.</p>
<p>This happened to me playing Dark Souls. I was already enticed by the uncaring nature of the world... I was the chosen undead, but no one seemed to care. But after a few hours I accidentally started a fight with the blacksmith and ended up killing him.</p>
<p>I was both shocked and impressed that this was even possible, and when I realised the ramifications meant progress was going to be so much harder, I restarted the game. But it didn't upset me, it was another part of my story - a story of failure sure - but a story I rarely found in any other games. It led to the world feeling even more autonomous and self governing, it made it more alienating but also more intriguing. This sort of world design isn't appropriate for many games but for a while It made me feel almost patronised by other RPGs theme park approach.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/30/2025-11-16-10_41_40-Publii.png" alt="" width="583" height="164" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_41_40-Publii-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_41_40-Publii-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_41_40-Publii-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/30/responsive/2025-11-16-10_41_40-Publii-xl.png 1024w"></figure>
<p>This response in the thread points towards the sort of simulationist promise that my blacksmith murder revealed. Some players won't care that everything is smoke and mirrors designed to ferry them along, but others want the world to live up to the affordances it pretends to have. In doing so the game offers the player more agency -  agency to potentially write a bad story, but agency nevertheless. And in the responses to my question I was delighted to see a number of folk who embraced this approach <em>"Yeah, let people mess up their games if they want to."</em></p>
<p>Of course we should signpost to players that killing key characters might cause a problem. But the freedom to get into those problems is valuable to some players in certain types of game-world. The negative consequences of actions might also be the start of an exciting emergent drama.</p>
<p>As one responder pointed out, it all really depends on the kind of game/world you want to build. But certainly for me - as an indie developer - this is an ongoing dialogue between what you are building and what emerges. Asking these questions in public is similar to trying out mechanics in prototypes, its a way to explore and think about design in an evolving context. I often don't know the form a game will take until its already way under development. I still don't know if it will feel right to allow the NPCs <em>in my game </em>to be killed. It is something I will have to work out as part of the development process, taking into account the solutions and responses listed above.</p>
<p>Ultimately I suspect that, as with most game dev, the constraints of time and budget put a stop to designers capabilities to support the consequences of killing the king. If its a choice between a solid guaranteed main path experience or more variable alternatives, we will often chose the former. But in a time where games seem to be taking less risks and following more formulas I can't help but cheer on the ones that dare to let players get off the rollercoaster and make mistakes.</p>
<p> </p>
<p> </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Indie Game Post-mortem - Highrook</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/game-post-mortem-highrook.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/game-post-mortem-highrook.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/25/HIghrook.jpg" medium="image" />
            <category term="Game Development"/>

        <updated>2025-10-19T17:52:10+01:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/25/HIghrook.jpg" alt="" />
                    A rocky road to Highrook So "The Horror At Highrook" has been&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/25/HIghrook.jpg" class="type:primaryImage" alt="" /></p>
                <figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/25/HIghrook-2.jpg" alt="" width="768" height="432" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/25/responsive/HIghrook-2-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/25/responsive/HIghrook-2-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/25/responsive/HIghrook-2-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/25/responsive/HIghrook-2-xl.jpg 1024w"></figure>
<h1>A rocky road to Highrook</h1>
<p>So "The Horror At Highrook" has been out for five months now and I am in that <em>in-between-projects-phase</em> where I am trying out various prototypes and putting together demos in order to test out ideas. While scribbling down design notes and trying out new art styles I thought it might be useful to write a short post-mortem of the game. If you are interested in taking a look at HIghrook after reading this you can find it at. <a href="https://store.steampowered.com/app/2836860/The_Horror_at_Highrook/" target="_blank" rel="noopener noreferrer">https://store.steampowered.com/app/2836860/The_Horror_at_Highrook/</a></p>
<p>The Horror at Highrook wasn't the game I expected to be making in 2024, it only became my main project as a result of a dramatic downturn in games industry financing that happened around 2023/3. Things became much more difficult to fund and unfortunately this is still largely the case.</p>
<p>Before this shift I was working on a deep systems rich tactical strategy game called Void Warden (In 2022/23). I had a substantial demo and I had started pitching around to try and get the money to finish the project. I had already spent a big chunk of my own cash on the prototype and I knew that I didn't have enough in the company accounts to finish making the game without support. Unfortunately, after almost 6 months of pitching and negotiations with various publishers It became clear that something was going on behind the scenes. </p>
<h1>Industry Woes</h1>
<p>Games were still being signed in 2023/24, but publishers had become a lot more wary and seemed to be increasingly interested in only 2 types of proposal.</p>
<p>Smaller publishers were looking for low cost breakout types of projects (think vampire survivors-likes, roguelikes deckbuilders etc). Whereas larger publishers were funding triple I projects that required larger teams, with budgets over over a million.</p>
<p>Mid range projects were not being funded or at least the opportunities for that scale of game had shrunk massively compared to a few years before. In addition to this a number of publishers and studios were beginning to close down. There was less money and opportunity around in general. Things weren't looking great for the industry.</p>
<p>Faced with this situation, I could have carried on pitching Void Warden in the hope of eventually getting a deal that worked (I had already turned down a few deals that I wasn't happy with) OR I needed to pivot to something else. If I was going to change track it had to be to a project I could finish with my own skills and my own money.</p>
<p>Amongst my many prototypes there was one game that fitted this requirements - Highrook. It was a game I'd wanted to make for years, and I'd already worked on various iterations of the idea. I had chosen Void Warden as my "next game" because at the time, I felt that the audience for Highrook was smaller and the game was harder to explain. But now I was looking to self publish I didn't have to worry about selling the idea to publishers. However, I did have to work out how I could produce the entire project myself.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/25/THAH_START-TASK.gif" alt="" width="640" height="360"></figure>
<h1>One man workshop</h1>
<p>Highrook was primarily a solo project, I wrote 100% of the code, wrote 90% of the narrative, drew 100% of the character art. I contracted folk to help with editing, QA, music, and community. But for most of the project it was just me labouring away. I enjoy my work so that in itself wasn't a problem, but as time went on I realised that I had put very little into my budget to deal with promotion, testing and community work. I can do those tasks myself, but I was already overloaded with the main work of the project. I didn't need money to finish the game, but I realised that having an extra source of funding would allow me to polish and promote Highrook properly.</p>
<h2>Back on the pitching train</h2>
<p>Reluctantly I started to pitch the project again, but this time I was effectively looking for what is usually called "finishing funds". I had a game that was about 80% complete and I wanted to release within around 6 months. I just needed a way to pay for marketing, QA, editing and some custom art.</p>
<p>I didn't really want a publisher to provide to-market services... I felt the project was so close to release that it would have been too hard for a 3rd party to get the right approach for what is quite a strange game. What I needed was funding to help me self publish. Eventually I found that support in Outersloth, who are without a doubt the best publishers I've ever worked with (Caveat, this is partly because they provide no services other than financial investment and I was pretty confident running the rest of the show myself).</p>
<p>So how did I take the game to 1.0 and release from here?</p>
<h2>Production Details</h2>
<p> </p>
<p><strong>Playtesting</strong>.</p>
<p>I ran a few playtests via my discord and they were invaluable. It cannot be said enough times, playtest as soon as you can and as often as you can! I managed the process via handing out override steam keys to people who signed up via a google form. (To be fair the community manager I hired at the time handled most of this - thanks Connor!). We set up a forum inside discord to allow users to post detailed bugs, and during each playtest (which ran for around 2 weeks at a time) I tried to do 2 or so rounds of fixes.</p>
<p><strong>Demo</strong></p>
<p>Having a demo out is also an amazing way to get feedback, fix bugs and gather interest. I had a demo up on steam for a year before launch and I updated the demo multiple times. Its often also a pre-requisite for getting your game features in steam festivals or even physical ones. Finally, it forces you to fix up and test the FTUE stuff you might normally avoid until much later!</p>
<p><strong>Festivals</strong></p>
<p>The game was in around 5, 6 festivals/showcases from the demo launch to the 1.0 release (a year). These had mixed results. Basically if a steam festival doesn't have front page feature it's not going to move the needle too much. In addition different festivals carry different weight, have more or less avid audiences etc. But normally they aren't much effort to do so its worth entering whatever you can.</p>
<p>I did one physical showcase, which I enjoyed doing and was an opportunity to connect with some publishers, industry peers and services. But I'm fairly sure that physical showcases (industry or commercial) have little effect on Wishlists or visibility.</p>
<p>I also firmly believe that Next fest is no longer the boon it once was, and its not necessarily worth planning around anymore. Sure you might get a boost, but with 1000s of games participating you need to also have your own visibility mechanisms running in parallel or you are just going to get lost in the crowd. Do at least one NextFest (usually just before release) but I think you get better results by appearing in showcases with more curation and a clearer audience.</p>
<p><strong>Press</strong></p>
<p>A lot has been said about the demise of traditional press, but I found that the few features I did get were valuable. In addition to just spreading the word its useful to be able to have pullquotes from reputable outlets to add to your page and marketing. I hired a freelancer to help push out the press releases on three main beats. Release date announcement, next fest and then release.</p>
<p><strong>Content Creators</strong></p>
<p>I got virtually no coverage from youtubers or streamers. The game is a slow, detailed narrative puzzle with a lot of reading and no voice acting, so its really not suited to that kind of media. I reached out to a few folk and my favourite youtuber did review it (Splattercat), his feature did get me some Wishlists so that was much appreciated.</p>
<p><strong>Community and Socials</strong></p>
<p>I think way too many developers fail to talk to their audience while making a game. It's easy to assume that because you like puzzle games, your puzzle game will be loved by the puzzle audience. This is simply not true, and the amount of wisdom and design feedback you can get from talking to your audience is brilliant. Playtesting doesn't count, because it still keeps you at arms length with a virtual clipboard. You should be excited enough about the game you are making to want to talk to other fans of the genre.</p>
<p>I spent a lot of time researching similar games to Highrook, and talking to their players. I did this less as a marketing ploy (in many cases I hardly mentioned it) but more as a way to understand the community and involve myself in the discourse. As a result I learned a whole load of things that made my game better.</p>
<p>Social media is a mess. I have no idea how effective it was for my game, I suspect not a lot. But as with all these things you should do it. Trying to get visibility is like throwing a die and hoping to land on a 6, the more dice you roll the more chance you have of that happening.</p>
<p><strong>Wishlists and stats</strong></p>
<p>Its easy to become obsessed with watching the stats, but that can get really stressful. I didn't announce my wishlist count on social media at all, despite being pretty happy with the figures close to launch. In the end Highrook launched at around 40k wishlists and has remained at around 80k outstanding wishlists since the end of the launch week. (this means as many new wishlists occur as sales/deletions)</p>
<p>Wishlist numbers are clung to because we don't have anything better to use. They are very variable in quality and the constant shifting nature of the industry and audience means that their predictive ability is also constantly shifting. Use them to help guide your projections but don't rely on those projections too heavily. Demo stats/community engagement are probably more useful, if a lot of folk are playing your demo for a long time or talking about the game in forums then you can usually take that as a good sign!</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/25/THAH_CHARACTER-NAMES.gif" alt="" width="640" height="360"></figure>
<h2>Release</h2>
<p>Highrook ended up being delayed by a couple of months, this was mainly due to being offered an opportunity to launch during a prominent steam festival (Ludonarracon). But the delay also coincided with two unexpected and difficult events. My dad died three months before release and I also ended up in hospital with kidney stones. Despite these circumstances the game came out on time and I've been really happy with the performance. It was well received by player and critics (New York times, The Guardian) and has made enough profit for me to plan another game. That's all most developers want really, to be able to keep on making things.</p>
<p>So what next? I'm not sure. The industry is still in a fragile state and funding is equally unstable. There is lots of noise about certain types of games or certain ways to develop. But I suspect that I will end up doing what I always do and following my nose, concentrating on things that I enjoy playing and making. Hopefully I can find a way to support that!</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Website Refresh</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/website-refresh.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/website-refresh.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/24/NullSetAV-2013-03-07-17-23-31-78-2.jpg" medium="image" />

        <updated>2025-10-08T17:19:33+01:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/24/NullSetAV-2013-03-07-17-23-31-78-2.jpg" alt="" />
                    This is just a short post to accompany the recent site revamp.
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/24/NullSetAV-2013-03-07-17-23-31-78-2.jpg" class="type:primaryImage" alt="" /></p>
                <figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/24/NullSetAV-2013-03-07-17-23-31-78.jpg" alt="" width="436" height="261" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/24/responsive/NullSetAV-2013-03-07-17-23-31-78-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/24/responsive/NullSetAV-2013-03-07-17-23-31-78-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/24/responsive/NullSetAV-2013-03-07-17-23-31-78-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/24/responsive/NullSetAV-2013-03-07-17-23-31-78-xl.jpg 1024w"></figure>
<p>This is just a short post to accompany the recent site revamp. I have used a wordpress driven blog for almost a decade, but like many folk I probably edit and update it with new info once a year! It didn't really make any sense to keep running such an overspecced system just in order to have a website available.  So over the last week I have moved most of the content over to a static site system and I'm now hosting the pages on GitHub.</p>
<p>I'd also like to return to producing some short tech blogs and explainers, and I found wordpress just too unwieldy for me to casually drop into and write a quick piece. I've tried medium and even tumblr, and although They both have decent interfaces and Tumblr has a really interesting community, but I'd quite like to be able to have everything in one place.. So that's the plan - I will port over some bits of orphan content that I have floating on other sites and then attempt to add to this version more frequently. More specifically I'd like to use the site to document the development of future game projects so that my short threads on tech stuff or art don't just vanish the day after I post them on bluesky.</p>
<p>It feels as if the golden age of social media is fading but at least I can maintain my own records here and hopefully some of the explainers and posts will be of use to others.</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Generating City Maps</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/generating-city-maps.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/generating-city-maps.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/23/1480563951209439235-FIwE-uDXMAwGfs4-2.jpg" medium="image" />
            <category term="Procedural Generation"/>
            <category term="Explainer"/>
            <category term="Archive"/>

        <updated>2025-10-06T10:50:22+01:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/23/1480563951209439235-FIwE-uDXMAwGfs4-2.jpg" alt="" />
                    This post explains how I coded a city map generator for a&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/23/1480563951209439235-FIwE-uDXMAwGfs4-2.jpg" class="type:primaryImage" alt="" /></p>
                <p>This post explains how I coded a city map generator for a project that never made it to full production. Some of the techniques involved are similar to the <a href="https://tomnullpointer.github.io/gh-pages/generating-a-fantasy-map.html">Fantasy Map Generator</a> I worked on, but this project was much more in-depth in terms of how regions of cells relate to each other and their final contents.</p>
<p>My main ambition with this project was to be able to generate a city map with sensible zoning, navigation and plot generation for housing etc.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/23/1473996160092225543-FHSu-LLWUAAQxC7-3.jpg" alt="" width="1200" height="687" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1473996160092225543-FHSu-LLWUAAQxC7-3-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1473996160092225543-FHSu-LLWUAAQxC7-3-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1473996160092225543-FHSu-LLWUAAQxC7-3-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1473996160092225543-FHSu-LLWUAAQxC7-3-xl.jpg 1024w"></figure>
<h1>Choosing a graph structure</h1>
<p>For the basic data structure I tried two different graph formats. The first attempt used a warped quad grid, as seen in games like <a href="https://store.steampowered.com/app/1291340/Townscaper/" target="_blank" rel="noopener noreferrer">Townscaper</a>. There is a good explainer of the process for generating such grids <a href="https://www.boristhebrave.com/docs/sylves/1/articles/tutorials/townscaper.html" target="_blank" rel="noopener noreferrer">here</a><a href="https://www.boristhebrave.com/docs/sylves/1/articles/tutorials/townscaper.html" target="_blank" rel="noopener noreferrer"></a>.</p>
<p>This format has the major advantage of every graph-cell being a quad and therefore always having 4 neighbours. This in turn leads to the possibility of generating objects within the cells that can make use of connective bitmasked tiling - a method of composing graphical tiles out of parts based on connections to their neighbours. you see this approach used frequently in 2d tile based games to allow for walls and paths to maintain sensible visual connections form one tile to the next (<a href="https://code.tutsplus.com/how-to-use-tile-bitmasking-to-auto-tile-your-level-layouts--cms-25673t" target="_blank" rel="noopener noreferrer">here is a good explainer)</a></p>
<p>Obviously knowing that each cell can have four walls helps enormously if you want to create continuous building structures (see Townscaper).</p>
<p>The image below shows how I split this warped quad grid into different city 'districts' (biomes). I also assign a margin of cells at the edges to be either 'ocean' or 'mountain' in order to give the city some definable edges.</p>
<p>These districts include a river which is defined more deliberately than the other districts (as it has to connect to the ocean and span across the majority of the map. </p>
<p>After the districts have been defined I link the cells within them together to create "blocks" that function a like interconnected apartment complexes etc. This 'joing up' pass was in preparation for a bitmasked style 3d or sprite based decorating phase.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/23/1661996108413906944-FxCYgCCXwAAbnLX.jpg" alt="" width="1020" height="852" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661996108413906944-FxCYgCCXwAAbnLX-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661996108413906944-FxCYgCCXwAAbnLX-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661996108413906944-FxCYgCCXwAAbnLX-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661996108413906944-FxCYgCCXwAAbnLX-xl.jpg 1024w"></figure>
<h1>Pathing</h1>
<p>In a similar approach to my Fantasy Map Generator, I place a loose grid of Key Road destinations (the white squares) across the graph and then connect those points with A* pathfinding. I remove some locations at random to make the result less grid-like .The roads follow cell edges and are naturally perturbed even if the underlying structure has no inbuilt penalties. I did however add a light amount of 2d noise to the pathfinding nodes in order to make the roads a bit more variable.</p>
<p>At this point that I became dissatisfied with the townscaper map approach. Unless the graph was very relaxed during generation, the cells could end up arranged with very acute edges (see some of the roads on the left of the map as an example). The other limitation of the approach is that all the cells remain roughly the same size (even more so if the graph is more relaxed), so all the basic cell plots are similar in size. </p>
<p>These limitations can be overcome with more work on relaxing the grid and merging cells, but I decided to try my old favourite alternative. The Voronoi Graph.</p>
<h1>Voronoi Approach</h1>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/23/1661991473062248448-FxCUSK4WcAAC6nE.png" alt="" width="910" height="752" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661991473062248448-FxCUSK4WcAAC6nE-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661991473062248448-FxCUSK4WcAAC6nE-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661991473062248448-FxCUSK4WcAAC6nE-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1661991473062248448-FxCUSK4WcAAC6nE-xl.png 1024w"></figure>
<p>The graph generation process is broadly similar to the warped quad mesh, except in a voronoi graph the cells are not restricted to being quads and the resulting mesh is less regular. This leads to more variance in the cell shapes and sizes across the map.</p>
<p>By placing the seed voronoi cell points in different configurations I could also create different styles of emergent layout; gridded, radial etc.</p>
<p>Roads are calculated in the same way as before, but after pathing is finished I run a function to add margins along every cell edge that bordered a path. In the image above you can see an issue where the end of roads taper to a single point, this isn't too hard to fix, I just didn't get around to it!</p>
<h1>Housing</h1>
<p>To create housing plots I take each cell and bisect it a number of times. These divisions are set to split the cells evenly (to avoid making slivers of geometry). I repeat the process several times - based on the initial size of the cell and a lower limit for the number/size of child plots produced&gt; This results in the creation of sub-plots and alley-ways within the original cell. </p>
<p>After the plots and subplots are defined I perform a basic square packing routine to see how many buildings I can fit into the remaining plot. This has an element of randomness included to ensure a nice range of building sizes (green plots become gardens) and any plots that are too narrow are eliminated (shown in red int he image above)</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/23/1480563951209439235-FIwE-uDXMAwGfs4.jpg" alt="" width="1200" height="624" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1480563951209439235-FIwE-uDXMAwGfs4-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1480563951209439235-FIwE-uDXMAwGfs4-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1480563951209439235-FIwE-uDXMAwGfs4-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/23/responsive/1480563951209439235-FIwE-uDXMAwGfs4-xl.jpg 1024w"></figure>
<p>Once the data structure is complete I render the map by placing building sprites and other decorations within the plots themselves. There are a range of rules to select the sprites based on biome/district type, frequency of building type, size of plot and proximity to other cell types etc. Each sprite is aligned to the plot direction and scaled to fit within. I then generate a network of meshes for the road networks and place the occasional lamppost at intersections. The results were pretty nice and certainly workable for any sort of medieval type city layout.</p>
<p>Finally, for fun I assigned some randomly generated names to the districts and add a bit of relevant typography.</p>
<p>I had to put this project on the back-burner due to financial constraints, but I'd love to return to it some day!</p>
<p> </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Generating a Fantasy Map</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/generating-a-fantasy-map.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/generating-a-fantasy-map.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/22/1470365535468761092-FGfHYSyWQAcjoWG.jpg" medium="image" />
            <category term="Procedural Generation"/>
            <category term="Explainer"/>

        <updated>2025-10-05T20:09:15+01:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1470365535468761092-FGfHYSyWQAcjoWG.jpg" alt="" />
                    This post presents the various stages of design and coding that I&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1470365535468761092-FGfHYSyWQAcjoWG.jpg" class="type:primaryImage" alt="" /></p>
                <p>This post presents the various stages of design and coding that I used for my 2021 Procjam entry <a href="https://tomnullpointer.itch.io/procjam21" target="_blank" rel="noopener noreferrer">https://tomnullpointer.itch.io/procjam21</a></p>
<p>I have always loved the look of D&amp;D/Tolkien style fantasy maps. I have always wanted to try generating this sort of pen and paper cartography. And as usual Procjam was a good excuse to spend a bit of time investigating the idea..</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1467141564870934531-FFxSBOfXIAgZcds.jpg" alt="" width="976" height="976" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141564870934531-FFxSBOfXIAgZcds-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141564870934531-FFxSBOfXIAgZcds-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141564870934531-FFxSBOfXIAgZcds-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141564870934531-FFxSBOfXIAgZcds-xl.jpg 1024w"></figure>
<h1>Starting with a Voronoi graph</h1>
<p>As with many of my terrain generation projects I decided to use a Voronoi dual graph as the basis of the map.</p>
<figure class="post__image"><img  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1467141574266171395-FFxTrAOXMAA1zK-.png" alt="" width="328" height="330" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141574266171395-FFxTrAOXMAA1zK--xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141574266171395-FFxTrAOXMAA1zK--sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141574266171395-FFxTrAOXMAA1zK--md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141574266171395-FFxTrAOXMAA1zK--xl.png 1024w"></figure><figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1467141584353435650-FFxVPxzXsAY5Kr6.png" alt="" width="332" height="329" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141584353435650-FFxVPxzXsAY5Kr6-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141584353435650-FFxVPxzXsAY5Kr6-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141584353435650-FFxVPxzXsAY5Kr6-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467141584353435650-FFxVPxzXsAY5Kr6-xl.png 1024w"></figure>
<p>I generate the graph from a fairly even distribution of initial cells and then mark border cells (red), ocean cells (blue), and terrain cells (green) - based on distance from the edges of the world. Later on i refined this process to result in more organic coast lines.</p>
<p>The code then positions several mountain peaks in the graph (set safely away from the coasts) and uses a simple diffusion loop to spread out the height, resulting in a range of altitude across the terrain.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1467569268355125252-FF3aE31XEAAI0Bg.png" alt="" width="779" height="778" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467569268355125252-FF3aE31XEAAI0Bg-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467569268355125252-FF3aE31XEAAI0Bg-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467569268355125252-FF3aE31XEAAI0Bg-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1467569268355125252-FF3aE31XEAAI0Bg-xl.png 1024w"></figure>
<p>The process then flags each cell with a biome tag biome based on a number of factors such as; distance from the ocean, altitude and underlying noise sampling.</p>
<h1>Pathfinding</h1>
<p>After setting up a the assignment of biomes across the map I pick some key cells to serve as destinations for a simulated road system. These are chosen by selecting points on a coarse superimposed grid that guarantees that each destination is always a certain distance from its nearest neighbour Any points that are too close or unreachable are discarded.</p>
<p>A simple A* pathfinding system is used to connect the key cells. To ensure a natural flow to the connecting paths, the map is first seeded with traversal penalties. Higher altitude cells have higher penalties and certain biomes (swamps for example) are made harder to cross. In addition to feature specific obstacles, all terrain  cells are treated with a light modifier based on a simple 2d noise array in order to add some randomness to the paths returned. </p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1468282156518629381-FGBgBVDWQAwUAcS.png" alt="" width="763" height="766" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282156518629381-FGBgBVDWQAwUAcS-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282156518629381-FGBgBVDWQAwUAcS-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282156518629381-FGBgBVDWQAwUAcS-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282156518629381-FGBgBVDWQAwUAcS-xl.png 1024w"></figure>
<p>Once a road network has been found the system deletes a few random points and connections (while ensuring that all destinations still have at least one path connecting them) to make the road network less grid like and encourage the occasional appearance of remote outposts etc.</p>
<p>After the roads are built, a similar system is used to create rivers. In this case the initial points are set to be at the top of the mountain areas close to the peaks used to drive the initial altitude diffusion. The rivers then proceed along cell edges, always trying to path to neighbouring cells with lower altitudes. This creates natural looking streams that end up in the ocean or a landlocked lake.</p>
<p>I use cell edges rather than  centres so that the rivers wont occupy the same space as any roads that might exist in the same locations.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1468282172922601478-FGBg1MUWQA03V13.png" alt="" width="1070" height="1003" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282172922601478-FGBg1MUWQA03V13-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282172922601478-FGBg1MUWQA03V13-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282172922601478-FGBg1MUWQA03V13-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1468282172922601478-FGBg1MUWQA03V13-xl.png 1024w"></figure>
<p>I could have continued to refine the data creation for the cells to give the map more expressive range, but as the project was part of a jam I decided to stop with my data structure work and start the visual 'cladding' of the map.</p>
<p>Each cell has a biome type which drives it to pull a sprite from a set defined by that biome. For instance, a cell above a specific height is a mountain and can use any of the 5 possible mountain sprites I had prepared. Again the number of cell types is fairly limited with locations such as hills,  forest, marshes, villages, towns, towers etc.</p>
<p>Finally I added a few parameters for the players to adjust, so that they could explore the range of maps the tool can generate.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/22/1470000731726004227-FGZ92moWQAQdpDH.png" alt="" width="1200" height="891" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1470000731726004227-FGZ92moWQAQdpDH-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1470000731726004227-FGZ92moWQAQdpDH-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1470000731726004227-FGZ92moWQAQdpDH-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/22/responsive/1470000731726004227-FGZ92moWQAQdpDH-xl.png 1024w"></figure>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Procedurally Generating Books</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/procedurally-generating-books.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/procedurally-generating-books.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/20/1732442787457446125-GArfeUqXgAAGnSi-2.png" medium="image" />
            <category term="Procedural Generation"/>
            <category term="Explainer"/>
            <category term="Archive"/>

        <updated>2025-10-05T18:51:33+01:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1732442787457446125-GArfeUqXgAAGnSi-2.png" alt="" />
                    This is a record of my day to day work on a&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1732442787457446125-GArfeUqXgAAGnSi-2.png" class="type:primaryImage" alt="" /></p>
                <div class="o q">
<div class="dw n dx dy dz ea">
<p id="df04" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">This is a record of my day to day work on a project for the <strong class="aee hp">2023 ProcJam game jam</strong>.</p>
<figure class="post__video"><iframe loading="lazy" width="560" height="314" src="https://www.youtube.com/embed/-OSQZ_6Sw_o" allowfullscreen="allowfullscreen" data-mce-fragment="1"></iframe></figure>
<h2 id="b7f5" class="aex aey ys as aez na afa nb ne nf afb ng nj nk afc nl no np afd nq nt nu afe nv ny aff cn" data-selectable-paragraph="">Day 0: preperations!</h2>
<h3 id="ee64" class="afg aey ys as aez ro afh rp ne su afi sv nj rq afj rr rs sy afk sz tb tc afl td tf afm cn" data-selectable-paragraph=""><strong class="bn">Acherons Archives: A PCG arcane bookshop</strong></h3>
<p id="14f5" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">Inspired by a combination of games, Wilmott's Warehouse, Happy Humbles burger farm, Strange Horticulture etc I want to make a bookshop management prototype. But all the books are procedurally generated.</p>
<p id="a9d9" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">The bookshop gets deliveries of arcane books each evening<br>you have all night to arrange them on the shelves/tables etc.<br>At dawn the shop opens and characters come in requesting books<br>a book has various categories — the more of these you can match the better the pay. Firstly i need to define how the books will be generated, so Im going to define a few sets of parameters for this.</p>
<h3 id="b412" class="afg aey ys as aez ro afh rp ne su afi sv nj rq afj rr rs sy afk sz tb tc afl td tf afm cn" data-selectable-paragraph="">Potential book categories for generation</h3>
<p id="9a6e" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph=""><strong class="aee hp">Publication Century:</strong><br>1900s<br>1800s<br>1700s<br>1600s</p>
<p id="6d72" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><strong class="aee hp">Rarity:</strong><br>1,2,3,4,5</p>
<p id="45b0" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><strong class="aee hp">Subjects:</strong> Books generally have 2 subjects<br><em class="afs">Oneiroscopy</em>: dreams : closed eye, cloud, candle on plate<br><em class="afs">Astromancy:</em> stars: stars, planets, constellations<br><em class="afs">Dendromancy:</em> trees : trees/roots/acorns/seeds<br><em class="afs">Pyromancy</em>: fire: fires, flames, embers, ovens<br><em class="afs">Necromancy:</em> dead : bones, skulls, coffins<br><em class="afs">Umbramancy:</em> shadows : shadows, cast by things<br><em class="afs">Lunascopy:</em> moon: different moon shapes<br><em class="afs">Lampadomancy:</em> light: lanterns, bulbs</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0__OPr8Tc8GOhNfv_Y.webp" alt="" width="563" height="789" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0__OPr8Tc8GOhNfv_Y-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0__OPr8Tc8GOhNfv_Y-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0__OPr8Tc8GOhNfv_Y-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0__OPr8Tc8GOhNfv_Y-xl.webp 1024w"></figure>
<p id="ebd9" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I made a pinterest board for inspiration on how these books could look, But primarily I think they need to be color coded per subject and have a layout that allows for me to place and modify icons/patterns/imagery that also relates to the book subject/age/rarity</p>
<p id="b702" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><a class="bi fg" href="https://www.pinterest.co.uk/tomnullpointer/acheron-archives/" rel="noopener ugc nofollow" target="_blank">https://www.pinterest.co.uk/tomnullpointer/acheron-archives/</a></p>
<p id="f096" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">because the jam is only a week long, and I’m unsure of how much time I might get to focus on it I’m going to simplify the generation a little. Im intending to use pixel graphics and a sort of oldskool psx look for the game. This means I can hopefully produce the required parts with less hassle. I am probably going to do this by taking some icons i already own and processing them in photoshop to fit the required format i will use in the game.</p>
<h2 id="620c" class="aex aey ys as aez na afa nb ne nf afb ng nj nk afc nl no np afd nq nt nu afe nv ny aff cn" data-selectable-paragraph="">Day 1: Setting things up</h2>
<h3 id="a820" class="afg aey ys as aez ro afh rp ne su afi sv nj rq afj rr rs sy afk sz tb tc afl td tf afm cn" data-selectable-paragraph="">Unity setup</h3>
<p id="24bd" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">So Im going to be using Unity for this prototype, Ive actually been learning Unreal recently, but my familiarity and speed in unity is just too good for me to ignore for a jam. So I'm starting how i usually do, by defining rough data structures that I will need, singletons to manage access to data definition libraries and then I’m going to start making the gameobject prefabs I’ll need to build the 3d objects.</p>
<p id="cb85" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I tend to make use of enums and scriptable objects A LOT, when defining the parts for my systems, so I'm going to start there too.</p>
<p id="0a43" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Here's the class for defining a single book, its mostly enums.</p>
<blockquote class="afz aga agb">
<p id="771c" class="aec aed afs aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">public class C_BookDefClass<br>{<br>public string m_BookName;<br>public int m_BookRarity;<br>public E_Era m_BookEra;<br>public E_Subject m_BookSubjectA;<br>public E_Subject m_BookSubjectB;<br>public C_Book m_pC_Book;<br>}</p>
</blockquote>
<p id="328a" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">This class will be built and filled by a “generator” script and then it will function as the basis for constructing the actual 3d book.</p>
<p id="3ef5" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Since each book is going to have at least 2 subjects, I decided to make each subject have its own little scriptable object class that lists the associated colours and material that could be used to represent that subject — Ill expand this later, but I know I’ll need something like this for the 3d construction phase.</p>
<blockquote class="afz aga agb">
<p id="1d03" class="aec aed afs aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">public class C_SO_SubjectResource : ScriptableObject<br>{<br>public E_Subject m_Subject;<br>public Material[] m_IconMats;<br>public Color[] m_Colors;<br>}</p>
</blockquote>
<h3 id="c96d" class="afg aey ys as aez ro afh rp ne su afi sv nj rq afj rr rs sy afk sz tb tc afl td tf afm cn" data-selectable-paragraph="">3D Book setup</h3>
<p id="3197" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">The next thing I did was to try and decide on a 3d format for the books.</p>
<p id="1f0a" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I know that I want to construct them in a sort of quilting 9-tile mosaic pattern. So i need a regular grid of sorts, and I want to be able to have guaranteed borders and a central divider that could be horizontal or vertical so a 5x5 grid looks like the minimum i can go for</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_Y5fXWMg87NqH55glrk2OkA.webp" alt="" width="467" height="472" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Y5fXWMg87NqH55glrk2OkA-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Y5fXWMg87NqH55glrk2OkA-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Y5fXWMg87NqH55glrk2OkA-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Y5fXWMg87NqH55glrk2OkA-xl.webp 1024w"></figure>
<p id="afd1" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Obviously this is a square! and the books are going to be rectangular, but I'm anticipating being able to just ratio up the Y axis and just work with each subsquare being a rectangle. Most books have a x to y of 1.5 so I'm going to aim for something like that.</p>
<p id="60c5" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">The nest decision is what pixel res i want to use for the subcells, and the overall book. I need to base the full book on multiples of whatever the smallest dimension I can use for the subsquares (5x subsquares across), and I'm thinking 32x32 per cell, which makes the overall book 160 pixels. If I'm stretching by 1.5 height then 32 becomes 48, which is probably ok for pixel snapped rescaling.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_Eo6Jl1Ni5tyMrk6rQv1yVg.webp" alt="" width="588" height="589" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Eo6Jl1Ni5tyMrk6rQv1yVg-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Eo6Jl1Ni5tyMrk6rQv1yVg-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Eo6Jl1Ni5tyMrk6rQv1yVg-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_Eo6Jl1Ni5tyMrk6rQv1yVg-xl.webp 1024w"></figure>
<p id="7508" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Do i need the same sort of grid for the back of the books? I’m not sure yet, probably not. Books tend to have less elaborate back covers. But I’ll need something!</p>
<p id="f714" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">The actual subtile images I am making by taking icons i already bought from the asset store and then cropping/rescaling and tweaking to fit in a 32x32 format. I'm also going to be using just black and white icons to start with, again to save time, I can always add in gradients/more colours later.</p>
<p id="af50" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">So now I have a potential grid layout for the book cover, I want to focus on 2 elements in the generated design, ICONS and BORDERS. The icons will represent the books subject matter and the borders will frame the icons..</p>
<p id="8882" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I'm going to focus on the borders first because I think they will be a bit harder to do..</p>
</div>
</div>
<div class="adw">
<div class="o q">
<div class="acu dv acv du acw dt ut agi uu agj dw n">
<div class="afu afv afw afx afy o ef">
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_e1MBSo3Ll3OWbH12.webp" alt="" width="233" height="418" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_e1MBSo3Ll3OWbH12-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_e1MBSo3Ll3OWbH12-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_e1MBSo3Ll3OWbH12-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_e1MBSo3Ll3OWbH12-xl.webp 1024w"></figure>
</div>
</div>
</div>
</div>
<div class="o q">
<div class="dw n dx dy dz ea">
<h3 id="8e00" class="afg aey ys as aez ro afh rp ne su afi sv nj rq afj rr rs sy afk sz tb tc afl td tf afm cn" data-selectable-paragraph="">First Problems!</h3>
<p id="ba90" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">Books are non-square, but if I want to be able to rotate the icons I'm using in the subsquares then I want those subsquares to be square! If I just scale the subcells (middle image) then the ration goes a bit off and things look stretched. So I tried to space out the actual anchors of each subcell (right image) which keeps the dimensions of each icon nice, but I get gaps in the borders and they don't meet the edges nicely. I have a few options — I can manually shift the border cells so that they hug the edges, I can make each cell a rectangle, or I can add another 2 rows of square cells in</p>
</div>
</div>
<div class="adw">
<div class="o q">
<div class="acu dv acv du acw dt ut agi uu agj dw n">
<div class="afu afv afw afx afy o ef">
<figure class="post__image"><img  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_J9ZV12MnqmrVVqhv_zM_4Q.webp" alt="" width="216" height="293" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_J9ZV12MnqmrVVqhv_zM_4Q-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_J9ZV12MnqmrVVqhv_zM_4Q-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_J9ZV12MnqmrVVqhv_zM_4Q-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_J9ZV12MnqmrVVqhv_zM_4Q-xl.webp 1024w"></figure><figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_EYgR5aWwgQE1ZD_M-BqBRg.webp" alt="" width="228" height="293" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EYgR5aWwgQE1ZD_M-BqBRg-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EYgR5aWwgQE1ZD_M-BqBRg-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EYgR5aWwgQE1ZD_M-BqBRg-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EYgR5aWwgQE1ZD_M-BqBRg-xl.webp 1024w"></figure><figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_DmXrCdXB5O9GaoQXYF5x4Q.webp" alt="" width="206" height="293" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_DmXrCdXB5O9GaoQXYF5x4Q-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_DmXrCdXB5O9GaoQXYF5x4Q-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_DmXrCdXB5O9GaoQXYF5x4Q-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_DmXrCdXB5O9GaoQXYF5x4Q-xl.webp 1024w"></figure>
</div>
</div>
</div>
</div>
<div class="o q">
<div class="dw n dx dy dz ea">
<p id="1ec7" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">In the end I'm opting for having 2 separate layers of decoration. One for the borders and another for the icons. This means I can save on some extra quads that would otherwise be taking up room. It also means I can potentially overlay layers.. I think this is a reasonable solution for now. I might add an extra cell into the margins if i feel I'm not getting enough value form the simpler setup</p>
</div>
</div>
<div class="adw">
<div class="o q">
<div class="acu dv acv du acw dt ut agi uu agj dw n">
<div class="afu afv afw afx afy o ef">
<figure class="post__image"><img  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_34cU1ChpRsz1F8rhX8ymIA.webp" alt="" width="202" height="290" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_34cU1ChpRsz1F8rhX8ymIA-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_34cU1ChpRsz1F8rhX8ymIA-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_34cU1ChpRsz1F8rhX8ymIA-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_34cU1ChpRsz1F8rhX8ymIA-xl.webp 1024w"></figure><figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_R5iZQxFcs0x5V2KCpkM0BA.webp" alt="" width="203" height="291" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_R5iZQxFcs0x5V2KCpkM0BA-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_R5iZQxFcs0x5V2KCpkM0BA-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_R5iZQxFcs0x5V2KCpkM0BA-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_R5iZQxFcs0x5V2KCpkM0BA-xl.webp 1024w"></figure>
</div>
</div>
</div>
</div>
<div class="o q">
<div class="dw n dx dy dz ea">
<p id="bc30" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">After some fiddling I've managed to get a basic generator up for the borders and background colours.</p>
<p id="6322" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">A while later I have added a similar generator 9 tile pattern setup for the icon area in the middle of the book. This is a set of reflected and rotated icons, which gives a specific geometric feel. I'm going to try and add some other alternative layout generators to not all the books have this centralised form.</p>
<h2 id="ff42" class="aex aey ys as aez na afa nb ne nf afb ng nj nk afc nl no np afd nq nt nu afe nv ny aff cn" data-selectable-paragraph="">Day 2: Landscape Layouts</h2>
<p id="661a" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">Ok so I started working on a landscape layout, because I don’t want all the books to have the same symmetrical star-like patterns. I think this layout has to be simpler than the other one, it can handle less variation before it kind of breaks. The key thing here is to find enough icons to make the variations interesting based on the items picked. But I also want the icons to fit with the books subject. I also need the “star” icon layouts to fit with subject matter too. I think this is going to be tricky, just the challenge of finding/editing enough icons. The landscape ones I'm taking from the <em class="afs">Isle Of Lore</em> set I bought from the asset store a year or so ago. Hopefully there's enough in there for me to use for each subject.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_B0sahWVOzqRBPfw2p7f42g.webp" alt="" width="207" height="311" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_B0sahWVOzqRBPfw2p7f42g-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_B0sahWVOzqRBPfw2p7f42g-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_B0sahWVOzqRBPfw2p7f42g-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_B0sahWVOzqRBPfw2p7f42g-xl.webp 1024w"></figure>
<p id="65a8" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">So what subjects have I defined? Lets see them again — Its going to be tricky to find icons to match. Each subject needs to have a distinct colour way and a recognisable set of icons that links to its theme. Its tricky to get things that stand out but don't clash! As usual I will be changing a lot of the specifics on the fly, depending on what looks good and what icons I can pull together.</p>
<p id="16e7" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><em class="afs">Oneiroscopy</em>: dreams : closed eye, cloud, candle on plate — dark purple<br><em class="afs">Astromancy:</em> stars: stars, planets, constellations — Light blue<br><em class="afs">Dendromancy:</em> trees : trees/roots/acorns/seeds — Brown/green<br><em class="afs">Pyromancy</em>: fire: fires, flames, embers, ovens — Orange<br><em class="afs">Necromancy:</em> dead : bones, skulls, coffins — Dark red<br><em class="afs">Umbramancy:</em> shadows : shadows, cast by things — Gray/ Dark blue<br><em class="afs">Lunascopy:</em> moon: different moon shapes — Light grey yellow<br><em class="afs">Lampadomancy:</em> light: lanterns, bulbs — Yellow</p>
<p id="cc81" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Here's a first pass at a landscape set for the pyromancy subject</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1731994938714300458-GAlIMPqW4AEoOD4.jpg" alt="" width="1200" height="720" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1731994938714300458-GAlIMPqW4AEoOD4-xs.jpg 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1731994938714300458-GAlIMPqW4AEoOD4-sm.jpg 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1731994938714300458-GAlIMPqW4AEoOD4-md.jpg 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1731994938714300458-GAlIMPqW4AEoOD4-xl.jpg 1024w"></figure>
<p id="cd5e" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">And here's the necromancy set mixed with the pyro set</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_lJZzkhgKLy2P4YFCpoz86w.webp" alt="" width="720" height="425" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_lJZzkhgKLy2P4YFCpoz86w-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_lJZzkhgKLy2P4YFCpoz86w-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_lJZzkhgKLy2P4YFCpoz86w-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_lJZzkhgKLy2P4YFCpoz86w-xl.webp 1024w"></figure>
<p id="765d" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I’m starting to look at the star layout code again now, with a view to linking it to subject themes in the same way I did for the landscape layouts. Here's the WIP of the astromancy set</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_f772NUCgH9p2UvduBXjPDA.webp" alt="" width="720" height="418" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_f772NUCgH9p2UvduBXjPDA-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_f772NUCgH9p2UvduBXjPDA-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_f772NUCgH9p2UvduBXjPDA-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_f772NUCgH9p2UvduBXjPDA-xl.webp 1024w"></figure>
<h2 id="1ba7" class="aex aey ys as aez na afa nb ne nf afb ng nj nk afc nl no np afd nq nt nu afe nv ny aff cn" data-selectable-paragraph="">Ok Day 3</h2>
<p id="9d14" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">Lets get back to it!</p>
<p id="cee3" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I spent an hour or so finishing the astromancy star-layout set and making a pyromancy version</p>
</div>
</div>
<div class="adw">
<div class="o q">
<div class="acu dv acv du acw dt ut agi uu agj dw n">
<div class="afu afv afw afx afy o ef">
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_ttdpxH7Z5QgN90iH.webp" alt="" width="640" height="371" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ttdpxH7Z5QgN90iH-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ttdpxH7Z5QgN90iH-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ttdpxH7Z5QgN90iH-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ttdpxH7Z5QgN90iH-xl.webp 1024w"></figure>
</div>
</div>
</div>
</div>
<div class="o q">
<div class="dw n dx dy dz ea">
<p id="12e8" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Now I can combine them for books that refer to both subjects.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_ZR1JXOTKqc0jm3KC.webp" alt="" width="720" height="417" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ZR1JXOTKqc0jm3KC-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ZR1JXOTKqc0jm3KC-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ZR1JXOTKqc0jm3KC-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_ZR1JXOTKqc0jm3KC-xl.webp 1024w"></figure>
<p id="9631" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I was going to move on to making the book titles but I thought I’d add the remaining 2 colour themes — Dendroscopy &amp; Necroscopy. I still only have 4 of the potential 8 subjects, but I think it will have to do for now. I can always add the others later.</p>
</div>
</div>
<div class="adw">
<div class="o q">
<div class="acu dv acv du acw dt ut agi uu agj dw n">
<div class="afu afv afw afx afy o ef">
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_5-60mz99PGCGpxsv.webp" alt="" width="640" height="367" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_5-60mz99PGCGpxsv-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_5-60mz99PGCGpxsv-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_5-60mz99PGCGpxsv-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_5-60mz99PGCGpxsv-xl.webp 1024w"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_doHuAb2gYmxShN0k.webp" alt="" width="640" height="369" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_doHuAb2gYmxShN0k-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_doHuAb2gYmxShN0k-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_doHuAb2gYmxShN0k-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_doHuAb2gYmxShN0k-xl.webp 1024w"></figure>
</div>
</div>
</div>
</div>
<div class="o q">
<div class="dw n dx dy dz ea">
<p id="ccc7" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">and a mix of these two</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_VxKLuf9cT7-BtYjjTd8uuA.webp" alt="" width="720" height="418" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_VxKLuf9cT7-BtYjjTd8uuA-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_VxKLuf9cT7-BtYjjTd8uuA-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_VxKLuf9cT7-BtYjjTd8uuA-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_VxKLuf9cT7-BtYjjTd8uuA-xl.webp 1024w"></figure>
<h3 id="def4" class="afg aey ys as aez ro afh rp ne su afi sv nj rq afj rr rs sy afk sz tb tc afl td tf afm cn" data-selectable-paragraph="">Title Generation</h3>
<p id="541c" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">So now I've got the icon generators in a state I’m happy with, I started to work on the title generator. As usual, I thought about going really complex and implementing some proper language stuff. But in the end I've opted for a bunch of word tables, specific to each theme — and then a set of 5 or so different sentence constructors.</p>
<p id="d6ee" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">For example I have</p>
<p id="cc19" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><em class="afs">“The adjective noun &amp; the adjective noun”</em></p>
<p id="41d1" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><em class="afs">“The adjective noun at/of/from the locationpartA LocationPartB”</em></p>
<p id="ba01" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><em class="afs">PersonNameA PersonNameB notes/poems/essays on adjective noun”</em></p>
<p id="3f4b" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">And so on, with a bunch of smaller variants within and some grammar sense included so it will mostly make sense. I need to make word tables for all the themes (4 so far) and then run a lot of generations just to check there's no really bad combinations. Its making some nice evocative phrases already, but its also making some pretty funny ones. I think I will leave these in because they are still believable, even if they are pretty silly.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1732442787457446125-GArfeUqXgAAGnSi.png" alt="" width="618" height="586" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1732442787457446125-GArfeUqXgAAGnSi-xs.png 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1732442787457446125-GArfeUqXgAAGnSi-sm.png 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1732442787457446125-GArfeUqXgAAGnSi-md.png 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1732442787457446125-GArfeUqXgAAGnSi-xl.png 1024w"></figure>
<p id="e7f2" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Tomorrow I really should try and get to the point where the books are actually 3d objects I can spawn in a game world!</p>
<h2 id="31de" class="aex aey ys as aez na afa nb ne nf afb ng nj nk afc nl no np afd nq nt nu afe nv ny aff cn" data-selectable-paragraph="">Day 4</h2>
<p id="d5c2" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">Today I really wanted to get started with the 3d aspect of the project. But First I felt I needed to fill in the remaining book theme word tables. That meant mainly finding Astromancy and Necromancy adjectives/nouns.</p>
<p id="d5c6" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Here's an example of the sort of material I'm using here</p>
<p id="a6bb" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><strong class="aee hp">Necromancy Nouns</strong></p>
<p id="8a92" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">corpse, cadaver, carcass, remains, body, relic, corpse, skeleton, corpus, deceased dog, carrion bird, dead body, ash pile, reliquiae, relic, shell, remnant, debris, hulk, shrine, dead animal, human remains, dead man, bonepile, zombie,skull, femur, ulna, nameless corpse, empty shell, Urn, Tombstone, Crypt, Charnel Pit, Crematorium, Morgue, Mortuary, Sacrifice, Plague Pit, shambler, wraith, phantom, ghost, ghoul, golem, beat, raven, crow, reaper, coffin, sarcophagus, grave, shroud</p>
<p id="709d" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph=""><strong class="aee hp">Necromancy Adjectives</strong></p>
<p id="7fb1" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">departed, deceased, late, lifeless, perished, fallen, inanimate, demised, lost, insensate, insentient, slain, slaughtered, breathless, inert, killed, murdered, still, cold, exanimate, lamented, unanimated, wasted, asleep, cadaverous, mortified, spiritless, buried, liquidated, silent, doomed, perishing, moribund, mortal, poisoned, near-death, fading, sinking, terminal, ghostly, declining, decaying, failing, foundering, withering, waning, crumbling, atrophying, sinking, declining, gravebound, disintegrating, fey, succumbing, collapsing, fated, passing, half-dead, expiring, dire</p>
<p id="9bbc" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Along with my previous 5+ sentence construction types this gives me a decent range of results. I’d like to improve it later, but its certainly fine for now.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_F9USkjV8F1ApnhnjlWEgFg.webp" alt="" width="720" height="230" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_F9USkjV8F1ApnhnjlWEgFg-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_F9USkjV8F1ApnhnjlWEgFg-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_F9USkjV8F1ApnhnjlWEgFg-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_F9USkjV8F1ApnhnjlWEgFg-xl.webp 1024w"></figure>
<p id="e532" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">So the next step was to make the books properly 3D, I just did a bunch of edits to the model and added some pages and a spine. I'm probably going to add some more info to these later, but again they are ok for now.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_7j3neYdegiYw45aMuNbZrA.webp" alt="" width="472" height="603" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_7j3neYdegiYw45aMuNbZrA-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_7j3neYdegiYw45aMuNbZrA-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_7j3neYdegiYw45aMuNbZrA-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_7j3neYdegiYw45aMuNbZrA-xl.webp 1024w"></figure>
<p id="ad10" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">So now I can generate these things in a 3d space. I spent a few mins mocking up a quick 3d environment to try it out…</p>
<p id="3db9" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">I spent most of the afternoon making the player able to carry, rotate, drop and throw the books. You can also put them on a shelf and it will try to snap the placed item onto the shelf next to its leftmost neighbour. It needs some refinement and I should probably do some different versions for table layouts or similar, but its probably ok for now. Books also have the title on their spine now, and they come is slightly varying sizes.</p>
</div>
</div>
<div class="adw">
<div class="o q">
<div class="acu dv acv du acw dt ut agi uu agj dw n">
<div class="afu afv afw afx afy o ef">
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_tQE5QQx24ZO9Ec8A.webp" alt="" width="640" height="310" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_tQE5QQx24ZO9Ec8A-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_tQE5QQx24ZO9Ec8A-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_tQE5QQx24ZO9Ec8A-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_tQE5QQx24ZO9Ec8A-xl.webp 1024w"></figure>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_FmGmMX8c-OqMNklw.webp" alt="" width="640" height="435" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_FmGmMX8c-OqMNklw-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_FmGmMX8c-OqMNklw-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_FmGmMX8c-OqMNklw-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_FmGmMX8c-OqMNklw-xl.webp 1024w"></figure>
</div>
</div>
</div>
</div>
<div class="o q">
<div class="dw n dx dy dz ea">
<h2 id="8393" class="aex aey ys as aez na afa nb ne nf afb ng nj nk afc nl no np afd nq nt nu afe nv ny aff cn" data-selectable-paragraph="">Day 5</h2>
<p id="46bf" class="pw-post-body-paragraph aec aed ys aee b aef afn aeh aei aej afo ael aem rq afp aeo aep sy afq aer aes tc afr aeu aev aew lv cn" data-selectable-paragraph="">So today I spent most of the time laying out the shop, putting shelves in, and creating a sort of delivery portal where the books will arrive. I've put in a mystical grandfather clock which shows the current in-game time. Each time it rolls around to dusk the next batch of books are delivered.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/1_EiZS9GTwHEnJjvkCZA8Orw.webp" alt="" width="720" height="380" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EiZS9GTwHEnJjvkCZA8Orw-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EiZS9GTwHEnJjvkCZA8Orw-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EiZS9GTwHEnJjvkCZA8Orw-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/1_EiZS9GTwHEnJjvkCZA8Orw-xl.webp 1024w"></figure>
<p id="2c1b" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">And when dawn arrives, the shops shutters roll up and customers appear. I haven’t got the customers making any requests yet though.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_hcYFLFM21w4h5VRF.webp" alt="" width="720" height="379" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_hcYFLFM21w4h5VRF-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_hcYFLFM21w4h5VRF-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_hcYFLFM21w4h5VRF-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_hcYFLFM21w4h5VRF-xl.webp 1024w"></figure>
<p id="73ed" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Of course I was advised that as this is a magic bookshop it needs a cat sitting by a roaring fire, so I wasted an hour or so making this..</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_GUKviAdQaOwbgf0Q.webp" alt="" width="720" height="421" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_GUKviAdQaOwbgf0Q-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_GUKviAdQaOwbgf0Q-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_GUKviAdQaOwbgf0Q-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_GUKviAdQaOwbgf0Q-xl.webp 1024w"></figure>
<p id="38b3" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">Finally, I decided to stress test the generator, and although it had no problem creating 1700 books , when they are all piled up in one place the game framerate crawls to like 15fps. I don't think you'd ever have this many books and when they are actually shelved the FPS is a lot smoother. I suppose I could put a cap on deliveries once you hit a maximum capacity of some sort.</p>
<figure class="post__image"><img loading="lazy"  src="https://tomnullpointer.github.io/gh-pages/media/posts/20/0_kMSK5jM4Xw0aypDQ.webp" alt="" width="720" height="380" sizes="(min-width: 760px) 660px, calc(93.18vw - 30px)" srcset="https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_kMSK5jM4Xw0aypDQ-xs.webp 320w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_kMSK5jM4Xw0aypDQ-sm.webp 480w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_kMSK5jM4Xw0aypDQ-md.webp 768w ,https://tomnullpointer.github.io/gh-pages/media/posts/20/responsive/0_kMSK5jM4Xw0aypDQ-xl.webp 1024w"></figure>
<p id="9a1b" class="pw-post-body-paragraph aec aed ys aee b aef aeg aeh aei aej aek ael aem rq aen aeo aep sy aeq aer aes tc aet aeu aev aew lv cn" data-selectable-paragraph="">This is officially the end of my mon-friday sprint on this jam. There's a few days over the weekend before the official end date, but I'm not sure how much work I will put into the prototype before the closing date of next Monday. However I’d really like to finish enough to make it playable, and if its fun I’d like to expand on it at a later date.</p>
</div>
</div>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Voxel Landscapes</title>
        <author>
            <name>me</name>
        </author>
        <link href="https://tomnullpointer.github.io/gh-pages/voxel-landscapes.html"/>
        <id>https://tomnullpointer.github.io/gh-pages/voxel-landscapes.html</id>
        <media:content url="https://tomnullpointer.github.io/gh-pages/media/posts/19/838510837706915840-C6L9JEYWMAEB8xO-2.jpg" medium="image" />
            <category term="Procedural Generation"/>
            <category term="IsoSurfaces"/>

        <updated>2025-10-05T12:31:43+01:00</updated>
            <summary>
                <![CDATA[
                        <img src="https://tomnullpointer.github.io/gh-pages/media/posts/19/838510837706915840-C6L9JEYWMAEB8xO-2.jpg" alt="" />
                    I ahve always been interested in different methods of terrain generation, from&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://tomnullpointer.github.io/gh-pages/media/posts/19/838510837706915840-C6L9JEYWMAEB8xO-2.jpg" class="type:primaryImage" alt="" /></p>
                <p>I ahve always been interested in different methods of terrain generation, from Geo-mipmaps to Cubspheres etc. One of the format I have worked the most with is probably noise-generated isosurfaces, wrapped with either a marching cubes algorithmn or an equivalent (Dual countouring, tetra etc)</p>
<p>I have used different variations of isosurface generation in projects such as Permutation Racer, Procedural Planets and even a gpu shader version.</p>
<p>Below is a short video of realtime generation via the graphics card and a small gallery of images from different iterations of my Terrain generation work.</p>
<figure class="post__video"><iframe loading="lazy" width="560" height="314" src="https://www.youtube.com/embed/fh_xKB75LBU" allowfullscreen="allowfullscreen" data-mce-fragment="1"></iframe></figure>
<div class="gallery-wrapper"><div class="gallery"  data-is-empty="false" data-translation="Add images" data-columns="3">
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/560198844714319873-B8Y53aoCAAAASS8.jpg" data-size="1200x659"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/560198844714319873-B8Y53aoCAAAASS8-thumbnail.jpg" alt="" width="768" height="422"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/560200604916924419-B8Y7etdCcAAeB6L.jpg" data-size="1200x645"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/560200604916924419-B8Y7etdCcAAeB6L-thumbnail.jpg" alt="" width="768" height="413"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/563845811214495746-B9MuwobCYAEejDn.jpg" data-size="1200x649"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/563845811214495746-B9MuwobCYAEejDn-thumbnail.jpg" alt="" width="768" height="415"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/574587423247712257-B_lYNnCUIAAtHz0.jpg" data-size="1200x681"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/574587423247712257-B_lYNnCUIAAtHz0-thumbnail.jpg" alt="" width="768" height="436"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/654032724219568128-CROXI4bWgAAtYxi.jpg" data-size="1200x726"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/654032724219568128-CROXI4bWgAAtYxi-thumbnail.jpg" alt="" width="768" height="465"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/768556592631582720-Cqp2HmsWcAIbiqC.jpg" data-size="1200x726"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/768556592631582720-Cqp2HmsWcAIbiqC-thumbnail.jpg" alt="" width="768" height="465"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/768566940910678016-Cqp_h2IWIAIx2cj.jpg" data-size="1200x737"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/768566940910678016-Cqp_h2IWIAIx2cj-thumbnail.jpg" alt="" width="768" height="472"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/769652011985952768-Cq5aZ1xXgAAUuCd.jpg" data-size="1200x698"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/769652011985952768-Cq5aZ1xXgAAUuCd-thumbnail.jpg" alt="" width="768" height="447"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/773274066941861889-Crs4pmXWEAEjQWC.jpg" data-size="1200x702"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/773274066941861889-Crs4pmXWEAEjQWC-thumbnail.jpg" alt="" width="768" height="449"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/775100139295965184-CsG1dMPWgAA0vws.jpg" data-size="1200x633"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/775100139295965184-CsG1dMPWgAA0vws-thumbnail.jpg" alt="" width="768" height="405"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/775100258820952064-CsG1j-ZWcAAQB5p.jpg" data-size="1200x714"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/775100258820952064-CsG1j-ZWcAAQB5p-thumbnail.jpg" alt="" width="768" height="457"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/838510837706915840-C6L9JEYWMAEB8xO.jpg" data-size="1200x714"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/838510837706915840-C6L9JEYWMAEB8xO-thumbnail.jpg" alt="" width="768" height="457"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/855756660941492224-C-BCFNLWsAApxIe.jpg" data-size="1200x726"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/855756660941492224-C-BCFNLWsAApxIe-thumbnail.jpg" alt="" width="768" height="465"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/891366238617817088-DF7E1GUXoAAoxYI.jpg" data-size="1200x686"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/891366238617817088-DF7E1GUXoAAoxYI-thumbnail.jpg" alt="" width="768" height="439"></a></figure>
<figure class="gallery__item"><a href="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/891406490237620224-DF7pVqHWAAAIaH2.jpg" data-size="1200x678"><img loading="lazy" src="https://tomnullpointer.github.io/gh-pages/media/posts/19/gallery/891406490237620224-DF7pVqHWAAAIaH2-thumbnail.jpg" alt="" width="768" height="434"></a></figure>
</div></div>
<figure class="post__video"><iframe loading="lazy" width="560" height="314" src="https://www.youtube.com/embed/qL0fcixT4z0" allowfullscreen="allowfullscreen" data-mce-fragment="1"></iframe></figure>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
            ]]>
        </content>
    </entry>
</feed>
