<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.1">Jekyll</generator><link href="https://pvigier.github.io/rss.xml" rel="self" type="application/atom+xml" /><link href="https://pvigier.github.io/" rel="alternate" type="text/html" /><updated>2022-11-12T11:39:13+01:00</updated><id>https://pvigier.github.io/rss.xml</id><title type="html">pvigier’s blog</title><author><name>Pierre Vigier</name></author><entry><title type="html">Room Generation using Constraint Satisfaction</title><link href="https://pvigier.github.io/2022/11/05/room-generation-using-constraint-satisfaction.html" rel="alternate" type="text/html" title="Room Generation using Constraint Satisfaction" /><published>2022-11-05T00:00:00+01:00</published><updated>2022-11-05T00:00:00+01:00</updated><id>https://pvigier.github.io/2022/11/05/room-generation-using-constraint-satisfaction</id><content type="html" xml:base="https://pvigier.github.io/2022/11/05/room-generation-using-constraint-satisfaction.html"><![CDATA[<p>Two weeks ago, I was part of <a href="https://www.roguelike.club/">Roguelike Celebration</a> and gave a talk on room generation using constraint satisfaction. It was great! Here is the video of my talk:</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/oVhq8V93gHM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>I don’t know why but the sound is a little jerky at times. :( And with my terrible accent, it may not be easy to understand certain parts. So I will transcript the talk in this article.</p>

<p>Before we start, you can get the demo code that accompanies the talk on my <a href="https://github.com/pvigier/room_generator">GitHub account</a> and the slides <a href="https://docs.google.com/presentation/d/1lECom7pLqrKIiVtetD_KEZHAtFeXWwcMqCItoteSEQ4/edit?usp=sharing">here</a>.</p>

<!--more-->

<h1 id="whats-a-room-generator">What’s a Room Generator</h1>

<p>So let’s start by defining, what’s a room generator.</p>

<table class="image-table">
  <thead>
    <tr>
      <th style="text-align: center">Before</th>
      <th style="text-align: center">After</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/laboratory_empty.png" alt="" width="250" /></td>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/laboratory_filled.png" alt="" width="250" /></td>
    </tr>
  </tbody>
</table>

<p>The input is simply any structure, it can be a building, a dungeon, anything. And it doesn’t matter what algorithm generated it. As it looks desperately empty, we want to fill the structure with objects, decorations and monsters. That’s what is room generation.</p>

<table class="image-table">
  <thead>
    <tr>
      <th style="text-align: center">Before</th>
      <th style="text-align: center">After</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/dungeon_empty.png" alt="" width="250" /></td>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/dungeon_filled.png" alt="" width="250" /></td>
    </tr>
  </tbody>
</table>

<table class="image-table">
  <thead>
    <tr>
      <th style="text-align: center">Before</th>
      <th style="text-align: center">After</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/cave_empty.png" alt="" width="250" /></td>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/cave_filled.png" alt="" width="250" /></td>
    </tr>
  </tbody>
</table>

<p>It works for buildings, but it also works well for caves or dungeons.</p>

<table class="image-table">
  <tbody>
    <tr>
      <td><img src="/media/img/room-generation-constraint-satisfaction/ruins.png" alt="" width="250" /></td>
      <td><img src="/media/img/room-generation-constraint-satisfaction/market_place.png" alt="" width="250" /></td>
    </tr>
  </tbody>
</table>

<p>And even for outdoor structures like the ruins on the left, or for the market place in this village.</p>

<h1 id="whats-a-constraint-satisfaction-problem-csp">What’s a Constraint Satisfaction Problem (CSP)?</h1>

<p>The generator is based on <a href="https://en.wikipedia.org/wiki/Constraint_satisfaction_problem">constraint satisfaction problems</a> that I will abbreviate as CSP.</p>

<p>A CSP is composed of three things:</p>

<ol>
  <li>There are <em>variables</em> which in room generation will represent the objects to place in the room.</li>
  <li>There is a <em>domain</em> for each variable. They are the finite sets of possible values for each variable. In room generation, the domains are simply all the possible positions for each object.</li>
  <li>There are the <em>constraints</em>, which are predicates on the values taken by the variables. In room generation, we may require that objects don’t overlap, that there are paths to access objects or we may want specific objects to be against a wall or in a corner.</li>
</ol>

<p>Solving a CSP is just finding an assignment that satisfies all the constraints.</p>

<p>We can notice two things. First, it’s very intuitive to use. If we want to have a shower next to a wall, we just have to add a constraint to do so. And we have strong guarantees on the output, which is always a nice thing in procedural generation.</p>

<h1 id="basic-solver-backtracking">Basic Solver: Backtracking</h1>

<p>Now, let’s see the basic algorithm to solve a CSP. It is called the <a href="https://en.wikipedia.org/wiki/Backtracking">“Backtracking Algorithm”</a>.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/backtracking.gif" alt="" class="center-image modal-image" /></p>

<p>This is just a refined brute force approach, we will enumerate all the possible assignments by recursively assigning the variables one after the other.</p>

<p>The only subtlety is that each time we try to assign a variable, we check the constraints, if they are satisfied we move to the next variable with a recursive call. But if the constraints are not satisfied or the recursive call fails, we move to the next value in the domain. And if there is no value left in the domain, we backtrack to the previous variable.</p>

<!--code-->
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">assignment</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">True</span>
    <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">domains</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
        <span class="n">assignment</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
        <span class="k">if</span> <span class="n">check_constraints</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">assignment</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">solve</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">assignment</span><span class="p">):</span>
                <span class="k">return</span> <span class="bp">True</span>
    <span class="k">return</span> <span class="bp">False</span>
</code></pre></div></div>

<p>It’s simple as you can see it’s less than 10 lines of code. And, it works very well, if there is a solution to the problem, it will find it. And with the constraints that I am presenting here, it is also very fast.</p>

<p>But as we always try the values in the same order, the solver is deterministic and will always output the same solution. Not ideal for a procedural generator.</p>

<h1 id="how-to-turn-that-into-a-generator">How to Turn That into a Generator</h1>

<p>However, it is very easy to fix this problem.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/backtracking_random.gif" alt="" class="center-image modal-image" /></p>

<p>We just have to shuffle the domains each time we want a new room.</p>

<p>If you are using an external library to solve your CSPs, just make sure that the selection process of values in domains is random.</p>

<p>Notice that the order in which the variables are processed doesn’t matter. And it doesn’t need to be random.</p>

<p>In fact, there is a heuristic called “Minimum Remaining Value” that can speed up the solver significantly. It consists in assigning the variables with smaller domains first because they are more constrained than the others.</p>

<h1 id="modeling-the-room">Modeling the Room</h1>

<p>The structure of the room is simply represented as a 2D or a 3D grid.</p>

<table class="image-table">
  <thead>
    <tr>
      <th style="text-align: center">Room</th>
      <th style="text-align: center">Grid</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/dungeon_structure.png" alt="" width="250" /></td>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/dungeon_grid.png" alt="" width="250" /></td>
    </tr>
    <tr>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/cave_empty.png" alt="" width="250" /></td>
      <td style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/cave_grid.png" alt="" width="250" /></td>
    </tr>
  </tbody>
</table>

<p>A 3D grid can be useful even for a 2D game. For example, in my game, I use a 3D grid with three layers to be able to have objects above others.</p>

<p>Cells in this grid can be in three different states:</p>
<ol>
  <li>The <em>empty</em> state, when a cell is free and can receive an object. These are the black tiles on the pictures.</li>
  <li>The <em>margin</em> state when a cell is free and we want it to remain free. These are the grey tiles on the pictures. We can notice that the connections to the other rooms are margin cells to make sure that these tiles remain free.</li>
  <li>The <em>full</em> state when a cell is already physically occupied by an object or a wall. These are the white tiles on the pictures.</li>
</ol>

<p>We may also want to add more data to a cell like marking it as a wall or a door which will be useful for certain constraints.</p>

<h1 id="modeling-the-objects">Modeling the Objects</h1>

<p><img src="/media/img/room-generation-constraint-satisfaction/Table.png" alt="" class="center-image modal-image" /></p>

<p>First, I find it more useful if a variable can represent not only one object but a group of objects. For example, a table with two chairs.</p>

<p>We could do that by using several variables and constraints to force the objects to be next to each others. But it would make the CSP much more complex and difficult to solve for no good reason.</p>

<p>Then, I specify a collision box which is the physical space that the objects will occupy.</p>

<p>And a margin box which will be the space around the objects that will need to remain free for gameplay or aesthetic reasons.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/Forge.png" alt="" class="center-image modal-image" /></p>

<p>For example, we want space in front of the forge to make sure the players can access it.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/Counter.png" alt="" class="center-image modal-image" /></p>

<p>Finally, I added the support for optional boolean masks for the two boxes to be able to add finer details if needed. As you can see with the counter.</p>

<h1 id="more-randomness">More Randomness</h1>

<p>I specify the lists of objects for my rooms in JSON and here is what looks like the definition of a variable.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"objects"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Painting1"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Painting2"</span><span class="p">,</span><span class="w"> </span><span class="err">...</span><span class="p">],</span><span class="w">
    </span><span class="nl">"constraints"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"NextToWallConstraint"</span><span class="p">:</span><span class="w"> </span><span class="p">{}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"range"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>First, we can notice that for a given variable I sample among a list of objects. In the example, I want to add a painting in the room and I choose among a list of them.</p>

<p>Then, let’s observe that the more we add variables to the CSP, the more difficult it will be for the solver to find a solution. And there are some objects like decorations, if the solver fails to place them, it’s not a big deal, the room stays valid.</p>

<p>So I separate the objects in two lists. The required objects which are the objects that the solver needs to place, otherwise the room loses its meaning.</p>

<p>And the optional objects, the solver will try to place them after the required objects but it will just try each value in their domains once and don’t backtrack if it fails to place an object.</p>

<p>What’s nice with optional objects as they are not required, we can make them more random with for example a probability of appearance or a random count. For example, the paintings have a range attribute and the solver will try to place between one and three in the room.</p>

<p>I will stress more on that at the end of the talk but optional objects are very useful to add variability in the generated rooms.</p>

<h1 id="unary-constraints">Unary Constraints</h1>

<p>Let’s look quickly at some of the constraints.</p>

<p>The simplest constraints are the unary constraints. They are the constraints that deal with only one variable.</p>

<p>For example, you want an object to be at a certain distance to a wall or in a corner.</p>

<p>The strategy for these constraints is just to remove the values in the domain that don’t satisfy the constraint before running the solver.</p>

<table class="image-table">
  <tbody>
    <tr>
      <td><img src="/media/img/room-generation-constraint-satisfaction/wall_constraint.png" alt="" /></td>
      <td><img src="/media/img/room-generation-constraint-satisfaction/corner_constraint.png" alt="" /></td>
    </tr>
  </tbody>
</table>

<p>So if you add a constraint that an object needs to be against the north wall, the domain will be only the purple cells of the first row. And if you want the object to be in a corner, the domain will be only the four purple cells in the corners.</p>

<h1 id="no-overlap-constraint">No Overlap Constraint</h1>

<p>The two next constraints are so important for room generation and they need to be implemented efficiently that I baked them directly in the solver.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/grid.png" alt="" class="center-image modal-image" /></p>

<p>To quickly check the overlap between objects, we will just reuse the grid we got as input but we will update it after each assignment or backtracking. After an assignment, we add the object, and after a backtracking, we remove the object.</p>

<h1 id="connectivity-constraint">Connectivity Constraint</h1>

<p>The connectivity constraint implies that all the free cells must be reachable.</p>

<p>You can check that using a simple <a href="https://en.wikipedia.org/wiki/Depth-first_search">depth-first search algorithm</a>. But you would need to run the DFS after each assignment which could be very expensive.</p>

<p>There is a little trick that can help a lot.</p>

<table class="image-table">
  <tbody>
    <tr>
      <td><img src="/media/img/room-generation-constraint-satisfaction/heuristic1.png" alt="" /></td>
      <td><img src="/media/img/room-generation-constraint-satisfaction/heuristic5.png" alt="" /></td>
    </tr>
  </tbody>
</table>

<p>If we look at the tiles around the object we just placed, and the free tiles are in one piece, then we are sure that the room is still connected.</p>

<table class="image-table">
  <thead>
    <tr>
      <th style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/heuristic3.png" alt="" /></th>
      <th style="text-align: center"><img src="/media/img/room-generation-constraint-satisfaction/heuristic4.png" alt="" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Don’t disconnect</td>
      <td style="text-align: center">Do disconnect</td>
    </tr>
  </tbody>
</table>

<p>However, if the surrounding is in two or more pieces then, we may have disconnected the rooms and we need to run the expensive DFS to check. This heuristic can save us most of the DFS calls.</p>

<h1 id="10000-bowls-of-oatmeal-problem">“10’000 bowls of oatmeal” Problem</h1>

<p>So far so good but we have an issue and it has a name, it’s called the “10’000 bowls of oatmeal Problem”.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/oatmeal.png" alt="" class="center-image modal-image" /></p>

<p>It says that we can very easily generate a lot of bowls of oatmeal by just changing the position or the rotation of each oat, but they will all look the same.</p>

<p>In our case:</p>
<ul>
  <li>the bowl is the room.</li>
  <li>the oatmeal is the objects.</li>
</ul>

<p><img src="/media/img/room-generation-constraint-satisfaction/inns_oatmeal.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>And as you can see on the picture, if we use the same input, the solver just move the objects in the rooms and we get a very similar output.</p>

<h1 id="solution-1-change-the-bowl">Solution 1: Change the Bowl</h1>

<p>The solution to the 10’000 bowls of oatmeal problem is of course, as all procedural generation practitioners know, to generate different bowls. Different shapes of bowls, different colors of bowls. And that way, all the bowls are different!</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/bowls.png" alt="" class="center-image modal-image" /></p>

<p>Of course, I’m kidding but there is an idea there to mitigate the issue.</p>

<p>If you change the context in which the objects are seen, the pattern may be less recognizable.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/inns_bowls.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>You can play on the shape of the structure, the textures, the skins of objects, etc.</p>

<h1 id="solution-2-dont-just-eat-oatmeal">Solution 2: Don’t Just Eat Oatmeal</h1>

<p>But the real solution to the 10’000 bowls of oatmeal problem is of course to change the inputs.</p>

<p>First, don’t overuse a room template. In particular, avoid using the same room template for two rooms close to each other.</p>

<p><img src="/media/img/room-generation-constraint-satisfaction/laboratories.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>Then, use alternative and optional objects. As explained, it helps to add variability in the room templates. That’s what we can see on the animation.</p>

<p>The best solution is to have a higher level generator to generate the inputs to make the room very different or so that they have more meaning.</p>

<p>For example, maybe you have to generate a house for a character. And you have already generated the hobbies for this character. Let’s say he’s fond of astronomy. That would be nice, if you can inject this meaning into the room generator and place a telescope in the house.</p>

<h1 id="tags-and-triggers">Tags and Triggers</h1>

<p>One nice and simple way to achieve this, that I am using in my game, is to use tags and triggers.</p>

<p>Your other generators will generate a context which is just a bag of tags. In our example, it will contain the tag “hobby_astronomy”.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"objects"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Telescope"</span><span class="p">],</span><span class="w">
    </span><span class="nl">"trigger"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hobby_astronomy"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Then, in the object description, you just add a trigger that will add the object only if the given tag is in the context.</p>

<p>That way, your generators are only loosely coupled. And if you need it you can make more advanced conditions for your triggers using propositional logic.</p>

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

<p>To conclude, I would say the using CSP in your generators is easy to reason about and easy to implement. But be careful of the 10’000 bowls of oatmeal issue.</p>

<p>Here are some references if you want to go further in some of the topics we dealt with:</p>
<ul>
  <li>On CSP Theory: Chapter 6 of Artificial Intelligence: A Modern Approach, Russel &amp; Norvig</li>
  <li>On “10’000 bowls of oatmeal” issue: <a href="https://galaxykate0.tumblr.com/post/139774965871/so-you-want-to-build-a-generator">So you want to build a generator…</a>, Kate Compton</li>
  <li>On Tags: <a href="http://www.gameaipro.com/GameAIPro3/GameAIPro3_Chapter38_Procedural_Level_and_Story_Generation_Using_Tag-Based_Content_Selection.pdf">Procedural Level and Story Generation Using Tag-Based Content Selection</a>, Jurie Horneman in Game AI Pro 3</li>
</ul>]]></content><author><name>pierre</name></author><category term="vagabond" /><category term="pcg" /><summary type="html"><![CDATA[Two weeks ago, I was part of Roguelike Celebration and gave a talk on room generation using constraint satisfaction. It was great! Here is the video of my talk: I don’t know why but the sound is a little jerky at times. :( And with my terrible accent, it may not be easy to understand certain parts. So I will transcript the talk in this article. Before we start, you can get the demo code that accompanies the talk on my GitHub account and the slides here.]]></summary></entry><entry><title type="html">Vagabond – Dungeon and Cave Generation – Part 3</title><link href="https://pvigier.github.io/2021/03/20/vagabond-dungeon-cave-generation-part3.html" rel="alternate" type="text/html" title="Vagabond – Dungeon and Cave Generation – Part 3" /><published>2021-03-20T00:00:00+01:00</published><updated>2021-03-20T00:00:00+01:00</updated><id>https://pvigier.github.io/2021/03/20/vagabond-dungeon-cave-generation-part3</id><content type="html" xml:base="https://pvigier.github.io/2021/03/20/vagabond-dungeon-cave-generation-part3.html"><![CDATA[<p><img src="/media/img/vagabond-dungeon-cave-generation-part3/dungeon_generation.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>Few weeks ago, I released the <a href="https://www.vagabondgame.com/2021/03/06/alpha-2-is-released.html">version alpha 2 of Vagabond</a>. One of the new features is the ability to mine ore veins in caves. This article is the follow-up of the two previous parts on dungeon generation (<a href="/2019/06/23/vagabond-dungeon-cave-generation.html">part 1</a>, <a href="/2019/06/30/vagabond-dungeon-cave-generation-part2.html">part 2</a>) where I explained how the structure of the caves were generated. However, the rooms were completely empty. In this article, I will explain how the rooms are filled with monsters, bosses, ore veins and treasures.</p>

<p>If you want to try the game and explore the procedurally generated caves presented here, you can play the free demo available on <a href="https://store.steampowered.com/app/1673090/Vagabond/">Steam</a>. If you have any feedback, do not hesitate to send me a message, I would be delighted to read it.</p>

<!--more-->

<h1 id="current-state">Current State</h1>

<p>To quickly recap where I stopped my dungeon generation algorithm, I use a BSP algorithm to create rooms, then I select a subset of the room edges as corridors, and I run a constrained cellular automaton to create walls with an organic shape. Finally, I run several post-processing steps to clean the result. You can see all this steps in the animation above.</p>

<p>So, as we can see on the image below, I have a clean abstract structure of the dungeon: clearly delimited rooms, a graph of rooms and a 2D array of booleans that represents the obstacles.</p>

<p><img src="/media/img/vagabond-dungeon-cave-generation-part3/previous_state.png" alt="" width="400" class="center-image modal-image" /></p>

<h1 id="assigning-room-types">Assigning Room Types</h1>

<p>My first step is to assign a type to each room. The type will then determine what the room will look like. I do that just before the cellular automaton pass to be able to add specific constraints if necessary.</p>

<p>Firstly, I choose the entrance room. There is a specific constraint: the door needs a wall on which it can be placed on as in the image below.</p>

<p><img src="/media/img/vagabond-dungeon-cave-generation-part3/door.png" alt="" width="400" class="center-image modal-image" /></p>

<p>To achieve this, I randomly choose a room with no corridor on its north edge and a constraint is added in the cellular automaton. You can see this constraint in the example below: it is the little black square in the north western room:</p>

<p><img src="/media/img/vagabond-dungeon-cave-generation-part3/cellular_automaton_initial_state.png" alt="" width="400" class="center-image modal-image" /></p>

<p>Then, when tiles are generated a door is placed there:</p>

<p><img src="/media/img/vagabond-dungeon-cave-generation-part3/door_result.png" alt="" width="400" class="center-image modal-image" /></p>

<p>Secondly, I randomly choose a room to be the boss room but I give more weight to the rooms far from the entrance so that the boss is rarely next to the entrance.</p>

<p>Finally, for the other rooms, a random type is chosen among the types for “normal” rooms.</p>

<p>Nothing fancy here, but if you have a large pool of room types and different types of dungeons with different subsets of room types, you can already have interesting results.</p>

<p>In the future, I may choose to use a more complex algorithm to choose room types. For instance, I can use the graph structure to place keys and locks.</p>

<h1 id="decorating-the-rooms">Decorating the Rooms</h1>

<p>To decorate the rooms, I reuse the room generator I originally created for the <a href="/2020/02/09/vagabond-building-generation.html">building generator</a>. It takes as input an abstract representation of the room as an array of booleans and a list of objects with their constraints, and it uses a <a href="https://en.wikipedia.org/wiki/Constraint_satisfaction_problem">CSP solver</a> to place the objects in the room. If you want to read more there is an entire section about it in <a href="/2020/02/09/vagabond-building-generation.html">this article</a>.</p>

<p>For now, as there is only caves in the world, the normal rooms are pretty simple: they are filled with monsters and ore veins. The entrance room only contains the door. The boss room contains a chest filled with a procedurally generated loot. But beware, it is protected by a boss which have boosted stats and a unique name procedurally generated by handcrafted grammars.</p>

<p>The only subtlety was that the room generator can only place objects in rooms not monsters. So I created an object “monster spawner” whose only purpose is to spawn monster to circumvent the issue.</p>

<p>Here is the final result:</p>

<p><img src="/media/img/vagabond-dungeon-cave-generation-part3/result.png" alt="" width="400" class="center-image modal-image" /></p>

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

<p>That’s all for this quick update on the dungeon generator. The dungeons are now fully playable!</p>

<p>In the future, I would like to make the dungeons more interesting, I have several avenues to explore in mind. On the one hand, I would like to work on the lore of dungeons: to generate stories and quests for each dungeons. On the other hand, I would like to make dungeons more interesting by theirself: maybe by adding keys, locks and puzzles, by having several stairs, and also by adding some special objects in corners and dead ends to encourage the players to explore.</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><category term="pcg" /><summary type="html"><![CDATA[Few weeks ago, I released the version alpha 2 of Vagabond. One of the new features is the ability to mine ore veins in caves. This article is the follow-up of the two previous parts on dungeon generation (part 1, part 2) where I explained how the structure of the caves were generated. However, the rooms were completely empty. In this article, I will explain how the rooms are filled with monsters, bosses, ore veins and treasures. If you want to try the game and explore the procedurally generated caves presented here, you can play the free demo available on Steam. If you have any feedback, do not hesitate to send me a message, I would be delighted to read it.]]></summary></entry><entry><title type="html">Procedural Death Animation With Falling Sand Automata</title><link href="https://pvigier.github.io/2020/12/12/procedural-death-animation-with-falling-sand-automata.html" rel="alternate" type="text/html" title="Procedural Death Animation With Falling Sand Automata" /><published>2020-12-12T00:00:00+01:00</published><updated>2020-12-12T00:00:00+01:00</updated><id>https://pvigier.github.io/2020/12/12/procedural-death-animation-with-falling-sand-automata</id><content type="html" xml:base="https://pvigier.github.io/2020/12/12/procedural-death-animation-with-falling-sand-automata.html"><![CDATA[<p>Hi everyone!</p>

<p>In this post, I will show you how I used falling sand automata to generate death animations for monsters of my game <a href="https://www.vagabondgame.com/">Vagabond</a>.</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/death_animations.gif" alt="" class="center-image modal-image" /></p>

<!--more-->

<h1 id="falling-sand-automata">Falling Sand Automata</h1>

<p>A falling sand automaton is a <a href="https://en.wikipedia.org/wiki/Cellular_automaton">cellular automaton</a> that simulates how grains of sand move due to gravity and create piles.</p>

<p>The rules are simple:</p>
<ul>
  <li>If the cell below a sand grain is empty, the sand grain moves to the empty cell (see (a)).</li>
  <li>If the cell below a sand grain is full but the cell at bottom left or the cell at bottom right is free, the sand grain moves there (see (b)). If both are free, choose one randomly.</li>
  <li>In the other cases, the sand grain does not move.</li>
</ul>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/automata_rules.png" alt="" class="center-image modal-image" /></p>

<p>If you want to know more about falling sand automata, you can look at this <a href="https://w-shadow.com/blog/2009/09/29/falling-sand-style-water-simulation/">article</a>.</p>

<p>With these simple rules you can obtain animations like this:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/sand.gif" alt="" class="center-image modal-image" /></p>

<h1 id="generating-death-animations">Generating Death Animations</h1>

<p>Now let us see how to use the falling sand automata to create death animations for the monsters.</p>

<p>The idea is to consider the non transparent pixels of the image as the sand grains and to make them fall to create a pile from the corpse of the monster. The only difference with the rules previously mentionned is that each sand grain has now a color. Here is what we obtain:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/death_animation.gif" alt="" class="center-image modal-image" /></p>

<p>In my opinion it is not very appealing for two reasons: the pile is very high and everything is falling at the same speed.</p>

<p>To fix the height issue, I use a 3D cellular automaton. I use several layers of the simple 2D cellular automaton. At the initial state, the image is in the middle layer, then the grain sands can not only move to bottom left and bottom right cells but also to the bottom cells of the previous and next layers:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/3d_automata_rules.png" alt="" class="center-image modal-image" /></p>

<p>To obtain an image from the 3D state of the cellular automaton, I project the state to 2D by taking, for each (i, j) coordinates, the first non transparent cell where k iterates the layers. Here is the result with three layers:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/death_animation_3d.gif" alt="" class="center-image modal-image" /></p>

<p>To improve the speed issue, I randomize the number of rows a grain fall in one step between 1 and \(n\). In practice, I use \(n = 2\) or \(n = 3\). Here is the result:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/death_animation_3d_speed.gif" alt="" class="center-image modal-image" /></p>

<p>It produces some holes during the fall as if the monster is disintegrating.</p>

<p>Finally, I skip some frames to have animations with exactly 6 frames. Here is the final result:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/death_animation_3d_speed_skip.gif" alt="" class="center-image modal-image" /></p>

<p>The grains are falling, on average, at a constant pace, while the gravity should make them accelerate. If you want you can skip more frames at the end of the animation to simulate that.</p>

<p>You can find the complete script on <a href="https://github.com/pvigier/lpc-scripts/blob/main/death_animation.py">GitHub</a>.</p>

<p>What’s nice is that if we are not happy with the result, we can rerun the script with a different seed to obtain a new animation. Here are several animations for the bat:</p>

<p><img src="/media/img/procedural-death-animation-with-falling-sand-automata/death_animation_seeds.gif" alt="" class="center-image modal-image" /></p>

<p>See you next time for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><category term="pcg" /><summary type="html"><![CDATA[Hi everyone! In this post, I will show you how I used falling sand automata to generate death animations for monsters of my game Vagabond.]]></summary></entry><entry><title type="html">Vagabond Enters in Alpha!</title><link href="https://pvigier.github.io/2020/12/11/vagabond-enters-in-alpha.html" rel="alternate" type="text/html" title="Vagabond Enters in Alpha!" /><published>2020-12-11T00:00:00+01:00</published><updated>2020-12-11T00:00:00+01:00</updated><id>https://pvigier.github.io/2020/12/11/vagabond-enters-in-alpha</id><content type="html" xml:base="https://pvigier.github.io/2020/12/11/vagabond-enters-in-alpha.html"><![CDATA[<iframe width="100%" height="400" src="https://www.youtube.com/embed/N69NKycYRV4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Hi everyone!</p>

<p>It has been a while since last time. I was working very hard on Vagabond to be able to announce you today that Vagabond is now in alpha!</p>

<p>The game is available on <a href="https://store.steampowered.com/app/1673090/Vagabond/">Steam</a> and its official website is <a href="https://www.vagabondgame.com/">vagabondgame.com</a>.</p>

<!--more-->

<p>I wrote an <a href="https://www.vagabondgame.com/2020/12/09/alpha-1-is-released.html">article</a> on the game website about this first version if you want to know more. All the announcements about the game (releases, changelogs, news, etc.) will be there, while the devlogs will stay here, on my personal website.</p>

<p>During these nearly nine months without post, I did many things and therefore I have many things to write so expect several articles in the comings days and weeks! :)</p>

<p>See you next time for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><summary type="html"><![CDATA[Hi everyone! It has been a while since last time. I was working very hard on Vagabond to be able to announce you today that Vagabond is now in alpha! The game is available on Steam and its official website is vagabondgame.com.]]></summary></entry><entry><title type="html">Vagabond – City Generation</title><link href="https://pvigier.github.io/2020/03/15/vagabond-city-generation.html" rel="alternate" type="text/html" title="Vagabond – City Generation" /><published>2020-03-15T00:00:00+01:00</published><updated>2020-03-15T00:00:00+01:00</updated><id>https://pvigier.github.io/2020/03/15/vagabond-city-generation</id><content type="html" xml:base="https://pvigier.github.io/2020/03/15/vagabond-city-generation.html"><![CDATA[<p>Hi everyone!</p>

<p>Last month, I had been working on city generation. The goal was to use buildings generated by my <a href="/2020/02/09/vagabond-building-generation.html">building generator</a>, to place them on the map and to link them using roads.</p>

<p>In this post, I will share with you the main ideas and techniques I used to achieve that.</p>

<p>Here is an animation showing the overall process:</p>

<p><img src="/media/img/vagabond-city-generation/city_generation.gif" alt="" class="center-image modal-image" /></p>

<!--more-->

<h1 id="building-placement">Building Placement</h1>

<p>To place the buildings I use an algorithm inspired by <a href="https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf">Bridson’s algorithm for Poisson Disk Sampling</a>. The idea is to place buildings one by one, and to place a new building next to an already placed building. The already placed building are tried from oldest to newest, this way, the cities have a concentric shape that is more organic.</p>

<p>Contrary to the original algorithm for Poisson Disk Sampling, I do not use a grid to check for collisions because the bounding box of each building is different, instead, I use a <a href="/2019/08/04/quadtree-collision-detection.html">quadtree</a>.</p>

<p><img src="/media/img/vagabond-city-generation/buildings.gif" alt="" width="400" class="center-image modal-image" /></p>

<h1 id="road-network-generation">Road Network Generation</h1>

<p>For road generation, I decided to reuse the algorithm I have already developed for roads between cities described <a href="/2019/05/19/vagabond-borders-rivers-cities-roads.html">here</a>: pathfind each door to each door using A* algorithm with a discount for already used road segments.</p>

<p>The problem is that contrary to the problem with cities where I had a dozen cities, here I may have several dozens buildings and as the number of pathfinding runs is quadratic with the number of buildings, it makes a huge difference. Moreover, for roads between cities, the graph was small, but for roads between buildings, the graph is a grid, it has a lot more nodes and edges.</p>

<p>For a city with 40 buildings, it took 200ms to compute the roads. I thought it was too much.</p>

<p>My solution to reduce the time spent is to reduce the number of pathfinding runs done. The idea is to find paths only between a building and its direct neighbors. To find the neighbors of buildings, I compute the <a href="https://en.wikipedia.org/wiki/Delaunay_triangulation">Delaunay graph</a> (I use <a href="https://github.com/pvigier/MyGAL">my library</a> to compute it, you can read more about it <a href="/2018/11/18/fortune-algorithm-details.html">here</a>). Here is the Delaunay graph of a set of buildings:</p>

<p><img src="/media/img/vagabond-city-generation/delaunay_graph.png" alt="" width="400" class="center-image modal-image" /></p>

<p>Now, I only have a linear number of pathfindings to perform. For a city with 40 buildings, it now took only 20ms, that is a big improvement!</p>

<p><img src="/media/img/vagabond-city-generation/road_network.png" alt="" width="400" class="center-image modal-image" /></p>

<p>We now have a road network but it lacks structure and it is a bad navigation mesh. To fix that, I modified the cost function of A* to penalize turns. Consequently, the paths are more straight and simpler as you can see:</p>

<p><img src="/media/img/vagabond-city-generation/road_network_turn_penalty.png" alt="" width="400" class="center-image modal-image" /></p>

<p>Currently, the edges are all of length one: they link two cells of the grid. But as the graph is composed of long straight lines, we can compress the graph by retrieving these lines. I achieve that using a DFS:</p>

<p><img src="/media/img/vagabond-city-generation/road_network_graph.png" alt="" width="400" class="center-image modal-image" /></p>

<p>This graph is way more suitable as a navigation mesh.</p>

<p>The final step is to represent these roads as tiles. I simply rasterize a disk of random radius and with a random offset on each cell of the road network to have something a bit chaotic:</p>

<p><img src="/media/img/vagabond-city-generation/road_network_tiles.png" alt="" width="400" class="center-image modal-image" /></p>

<p>And here is the final result with buildings displayed:</p>

<p><img src="/media/img/vagabond-city-generation/road_network_buildings.png" alt="" width="400" class="center-image modal-image" /></p>

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

<p>Let us look at some cities generated by the generator to conclude this blog post:</p>

<p><img src="/media/img/vagabond-city-generation/cities.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>The results are still a bit monotonous but it will be better when the buildings will be more distinct and new types of structures such as market places, wells or castles will be added.</p>

<p>The next step is to populate the cities with villagers.</p>

<p>All the assets used in this post were made by <a href="https://opengameart.org/">OpenGameArt</a> artists, you can find them <a href="https://opengameart.org/content/vagabonds-assets">there</a>.</p>

<p>See you next time for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><category term="pcg" /><summary type="html"><![CDATA[Hi everyone! Last month, I had been working on city generation. The goal was to use buildings generated by my building generator, to place them on the map and to link them using roads. In this post, I will share with you the main ideas and techniques I used to achieve that. Here is an animation showing the overall process:]]></summary></entry><entry><title type="html">Vagabond – Building Generation</title><link href="https://pvigier.github.io/2020/02/09/vagabond-building-generation.html" rel="alternate" type="text/html" title="Vagabond – Building Generation" /><published>2020-02-09T00:00:00+01:00</published><updated>2020-02-09T00:00:00+01:00</updated><id>https://pvigier.github.io/2020/02/09/vagabond-building-generation</id><content type="html" xml:base="https://pvigier.github.io/2020/02/09/vagabond-building-generation.html"><![CDATA[<p>Hi everyone!</p>

<p>These last weeks, I had been working on building generation. I want to share with you the main ideas of my method, some insights, and my results!</p>

<p>Here is a quick glimpse of the overall process:</p>

<p><img src="/media/img/vagabond-building-generation/building_generation.gif" alt="" class="center-image modal-image" /></p>

<!--more-->

<h1 id="floor-plan-generation">Floor Plan Generation</h1>

<p>The first step is to generate the floor plan that will be the input for the next steps.</p>

<p>There were a lot of trials and errors at this step. I wanted something flexible, that is, the generator takes as input a list of rooms that the building must contain and it generates a floor plan, whatever the number of rooms or their shape is. Thus, we cannot rely on some patterns to arrange the rooms. Moreover, I wanted the generator to be robust and always return good enough shapes.</p>

<p>I decided to use an incremental method: to place the rooms one by one.</p>

<p>My first attempt was to maintain the frontier of the building as a list of edges. Then, at each step, I would select an edge and grow a new room from it. The size of the new room was randomly generated. It was a terrible idea, it creates a lot of holes in the building shapes.</p>

<p><img src="/media/img/vagabond-building-generation/floor_plans_edges.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>My second attempt to prevent the strange holes from occuring was to use a grid to place the rooms. A room would occupy exactly one cell and the frontier is now the set of cells that are neighbors to the already selected cells. Again, at each step, we select a cell in the frontier and add a room in this cell. But to have more variety, I chose to have columns with variable widths and rows with variable heights. It was a terrible idea, again. The issue, this time, is that all the rooms in the same column have the same width, and all the rooms in the same row have the same height, it was a bit weird. Moreover, the topology of the building was too simple for my liking.</p>

<p><img src="/media/img/vagabond-building-generation/floor_plans_variable.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>In my third attempt, I decided to fix the size of columns and rows, but the rooms can now occupy several cells which will allow more complex topologies. I think this is a good trade-off: we lose a bit of variety on the room shapes but having predictable room sizes will ease the next steps. Moreover, I find this solution particularly clean and simple to implement. I am still wondering why this was not the first thing I tried.</p>

<p><img src="/media/img/vagabond-building-generation/floor_plans.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>I want to give a bit more insight into how a cell is chosen from the frontier. If you select a random cell, there may be a collision problem (if the room takes several cells) and after several steps, you may have something very weird. To prevent that, all the cells in the frontier are tried, we check for collisions and a score is attributed to the configuration. Then, I randomly pick a cell among the cells that have the highest scores.</p>

<p>One question remains: how to determine the score? Well, in the beginning, I tried to quantify the beauty of a floor plan, and it was hard to find good criteria. Then, I changed my way of thinking instead of trying to select the most beautiful floor plans, I penalize ugly and weird floor plans. And, it reveals to be way simpler to craft a score function that penalizes weird floor plans than one that finds the beautiful ones. I mainly penalize two things: extreme ratios, I don’t want too flattened buildings, and holes. Just with these two criteria, most weird building shapes are ruled out and the ones that are kept are at least correct.</p>

<h1 id="interior-generation">Interior Generation</h1>

<p>Now, that we have floor plans, we can generate floors and walls. As the generator is for a 2D game, I have to take care of the projection and let enough place between rooms to be able to put the walls.</p>

<p>I can assign different tiles for the floor and walls of the rooms. But for now, they are uniform for the whole building.</p>

<p><img src="/media/img/vagabond-building-generation/interiors.gif" alt="" width="400" class="center-image modal-image" /></p>

<h1 id="exterior-generation">Exterior Generation</h1>

<p>Oddly, this was the first thing, I implemented. Even before I finished floor plan generation. Surely because I thought it was one of the easiest steps. In fact, there are some subtleties so that everything works well with 2D tiles.</p>

<p>One preprocessing step that reveals useful is to merge rooms to have larger rectangle parts. To achieve that, I used a <a href="https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/">greedy meshing</a> algorithm.</p>

<p><img src="/media/img/vagabond-building-generation/building_parts.gif" alt="" width="400" class="center-image modal-image" /></p>

<h2 id="wall-generation">Wall Generation</h2>

<p>Nothing really difficult there. I just draw the bottom walls of building parts.</p>

<p><img src="/media/img/vagabond-building-generation/walls.gif" alt="" width="400" class="center-image modal-image" /></p>

<h2 id="roof-generation">Roof Generation</h2>

<p>Roofs are more challenging. I studied the different types of roofs a bit. For now, two of the simplest types, flat roofs and mansard roofs, are supported.</p>

<p><img src="/media/img/vagabond-building-generation/roofs.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>I also have an experimental implementation of hip roof generation. Hip roofs are more complex because the tiling depends on the width and you must have a special case for the transition between two building parts. I will finish that later.</p>

<p><img src="/media/img/vagabond-building-generation/hip_roof.png" alt="" width="400" class="center-image modal-image" /></p>

<h1 id="room-generation">Room Generation</h1>

<p>Placing objects in the rooms is by far the most interesting part of this generator.</p>

<p>The first step is to define object groups, it may be one object alone or objects that should always be placed together, for instance, a table with chairs. For each object group, I will also associate a collision box and a margin box. The collision box corresponds to the tiles used by the object group while the margin box corresponds to the tiles that must be free around the group to be able to interact with the objects in the game.</p>

<p>Here you can visualize the collision box and the margin box for some objects:</p>

<p><img src="/media/img/vagabond-building-generation/object_boxes.png" alt="" width="400" class="center-image modal-image" /></p>

<p>Then, for each room definition, I make a difference between objects that are necessary and those that are optional. Necessary objects must be placed in the room, otherwise, the room is invalid. For example, there must be a bed in a bedroom, if we fail to place one there, then the generator fails. On the contrary, decorations, such as a pot with a plant, are optional and if we fail to place them, the room is still valid.</p>

<p>Besides, for each object group in the room, constraints can be assigned to it. Some constraints I implemented are: distance to a wall, in a corner of the room or horizontally centered in the room.</p>

<p>To place, necessary objects, I use a <a href="https://en.wikipedia.org/wiki/Constraint_satisfaction_problem">CSP</a> solver I have designed especially for this problem. In particular, it can check very quickly for collisions between objects and that the room is connected, that is, we can access to all objects and doors in the room. My CSP solver uses a simple <a href="https://en.wikipedia.org/wiki/Backtracking">backtracking</a> algorithm.</p>

<p>One important thing, for procedural generation, is to keep the solver “non-deterministic” i.e. it does not always return the same solution. Otherwise, it would be boring. To achieve that, I simply shuffle the domains, the sets of positions tried for each object group.</p>

<p>On the contrary, for optional objects, I do not use the CSP solver. This allows keeping the problems relatively small and easy for the solver and thus to solve them very quickly. For each optional object, I just pick randomly one of its valid position and if there are none, it is not placed and I try the next object.</p>

<p>Here are some living rooms generated with the same set of parameters:</p>

<p><img src="/media/img/vagabond-building-generation/living_rooms.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>And some buildings with objects:</p>

<p><img src="/media/img/vagabond-building-generation/objects.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>For now, there are only three different types of rooms: a bedroom, a kitchen and a living room. I will design more later.</p>

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

<p>Let us look at some buildings generated by the generator to conclude this blog post:</p>

<p><img src="/media/img/vagabond-building-generation/buildings.gif" alt="" class="center-image modal-image" /></p>

<p>The whole process is quite fast, under 1ms for a whole building, it will allow me to generate hundreds to thousands of buildings during world generation!</p>

<p>Most of the ideas described here are very simple, but as we say, the devil is in the detail, and there were a lot of details there!</p>

<p>Now, that we have buildings, the next step is obviously to generate cities. It is one of the last steps before the release of the alpha version. I am so excited to eventually share my work with people! :)</p>

<p>All the assets used in this post were made by <a href="https://opengameart.org/">OpenGameArt</a> artists, you can find them <a href="https://opengameart.org/content/vagabonds-assets">there</a>.</p>

<p>See you next time for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><category term="pcg" /><summary type="html"><![CDATA[Hi everyone! These last weeks, I had been working on building generation. I want to share with you the main ideas of my method, some insights, and my results! Here is a quick glimpse of the overall process:]]></summary></entry><entry><title type="html">Vagabond – Let’s Fight</title><link href="https://pvigier.github.io/2019/11/18/vagabond-let-s-fight.html" rel="alternate" type="text/html" title="Vagabond – Let’s Fight" /><published>2019-11-18T00:00:00+01:00</published><updated>2019-11-18T00:00:00+01:00</updated><id>https://pvigier.github.io/2019/11/18/vagabond-let-s-fight</id><content type="html" xml:base="https://pvigier.github.io/2019/11/18/vagabond-let-s-fight.html"><![CDATA[<p>Hi everyone!</p>

<p>It’s been a long time since the last devlog.</p>

<p>I have been mainly working on monsters and combats. I created a lot of components and systems but nothing which deserves its own blog post. Let’s see the improvements.</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-let-s-fight/attack_dungeon_updated.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<!--more-->

<h1 id="monsters">Monsters</h1>

<p>Firstly, I ingested several monsters, even if you will only see bats in this devlog. And implemented a very basic artificial intelligence: they wander in rooms of dungeons.</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-let-s-fight/monster_wander.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<h1 id="combat-system">Combat System</h1>

<p>It was then time to kick their ass. I had to modify the physics engine to support hurtboxes and hitboxes.</p>

<p>Then, I had to improve the way animations are synchronized between clients which requires me to do huge changes in the codebase.</p>

<p>Eventually, it was possible to attack the monsters:</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-let-s-fight/combat_system.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<p>Monsters receive a small knockout just after being hit.</p>

<p>It is not impressive yet, but with some particle effects and sound effects, it should be funnier.</p>

<h1 id="hud">HUD</h1>

<p>It had been weeks, I was postponing the work on the user interface. I rolled up my sleeves and made a basic HUD to display the target monster name and health.</p>

<p>Moreover, I change the inputs. Now, everything is done with the mouse. Thus, I created a custom golden cursor you can see in the video below.</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-let-s-fight/attack_interface.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<p>I also started to do preparatory work on a drag and drop system for the future inventory interface.</p>

<h1 id="cave-generation-update">Cave Generation Update</h1>

<p>Finally, I worked on the cave generator. I fixed pernicious bugs occurring during tile generation. Moreover, I added some cycles during maze generation. I am very happy with the results:</p>

<p><img src="/media/img/vagabond-let-s-fight/dungeon_generation_v3.gif" alt="" width="400" class="center-image modal-image" /></p>

<p>I shared this animation on Reddit on <a href="https://www.reddit.com/r/gamedev/comments/dx95df/cave_generation_using_bsp_and_cellular_automaton/">r/gamedev</a>, I received many kind comments, it was very motivating!</p>

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

<p>That’s all for this devlog.</p>

<p>I am currently working on items, inventory, and characters. I think it will be a great step for the game and I hope I will have a lot of nice stuff to show.</p>

<p>See you next time for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><summary type="html"><![CDATA[Hi everyone! It’s been a long time since the last devlog. I have been mainly working on monsters and combats. I created a lot of components and systems but nothing which deserves its own blog post. Let’s see the improvements. Sorry, your browser doesn't support embedded videos.]]></summary></entry><entry><title type="html">Vagabond – Teleporting Into Dungeons</title><link href="https://pvigier.github.io/2019/10/20/vagabond-teleporting-into-dungeons.html" rel="alternate" type="text/html" title="Vagabond – Teleporting Into Dungeons" /><published>2019-10-20T00:00:00+02:00</published><updated>2019-10-20T00:00:00+02:00</updated><id>https://pvigier.github.io/2019/10/20/vagabond-teleporting-into-dungeons</id><content type="html" xml:base="https://pvigier.github.io/2019/10/20/vagabond-teleporting-into-dungeons.html"><![CDATA[<p>Hi everyone!</p>

<p>Last two weeks, I was working on teleporting the players from one map to another. I will use this system to enter into dungeons, buildings, etc. and also to leave them. But it was not that easy to implement as it required some deep architectural changes in the game engine.</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-teleporting-into-dungeons/dungeon_entrance.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<!--more-->

<h1 id="a-multilayer-world">A Multilayer World</h1>

<p>In a 2D top-down game, it is hard to make an open-world with only one map like Minecraft. It would mean it is not possible to go underground or upstairs in buildings. Thus, I need to be able to display several maps and to go from one map to another using <em>doors</em>.</p>

<p>But it is easier said than done. In particular for a multiplayer game, as there may be several players on different maps, and so you must manage several maps simultaneously. While in a single-player game, you just keep the map where the player is currently on.</p>

<p>Moreover, when I store the position of an entity, not only I must store its <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> coordinates but also the map on which it is. More precisely, as the world can be quite large, it is cut in chunks so I store the indices of the chunk on which the entity is currently on.</p>

<p>Previously, I was using a data structure like this one:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">Position</span>
<span class="p">{</span>
    <span class="kt">float</span> <span class="n">x</span><span class="p">;</span>
    <span class="kt">float</span> <span class="n">y</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>And now, I am using something like that:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">Position</span>
<span class="p">{</span>
    <span class="c1">// Chunk id</span>
    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">j</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">k</span><span class="p">;</span>
    <span class="c1">// Offset</span>
    <span class="kt">float</span> <span class="n">x</span><span class="p">;</span>
    <span class="kt">float</span> <span class="n">y</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>This little change in the <code class="language-plaintext highlighter-rouge">Position</code> data structure forced me to rework my physics engine and other systems.</p>

<h1 id="teleportation-system">Teleportation System</h1>

<p>Once these architectural changes were done, I was ready to implement my teleportation system. As I use an entity-component-system, implementing this was just creating a new component and a new system.</p>

<p>The door component just contains a destination:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">DoorComponent</span>
<span class="p">{</span>
    <span class="n">Position</span> <span class="n">destination</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The door system receives collision messages from the physics engine. If a collision occurs between an entity that has a door component and another entity, it transports the other entity at the door destination. Nothing difficult, once all the other systems are in place.</p>

<p>And it works well, even in multilayer mode where there are two players on different maps:</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-teleporting-into-dungeons/dungeon_entrance_multiplayer.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<h1 id="dungeon-entrances">Dungeon Entrances</h1>

<p>Finally, I worked on a dungeon entrance generator. Nothing fancy but it works well and you can change the materials of the walls to match the one used in the dungeon:</p>

<p><img src="/media/img/vagabond-teleporting-into-dungeons/dungeon_entrances.gif" alt="" class="center-image modal-image" /></p>

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

<p>That’s all for this devlog.</p>

<p>Next week, I will work on monsters and add them inside the dungeons. That will be a big step!</p>

<p>See you next week for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><summary type="html"><![CDATA[Hi everyone! Last two weeks, I was working on teleporting the players from one map to another. I will use this system to enter into dungeons, buildings, etc. and also to leave them. But it was not that easy to implement as it required some deep architectural changes in the game engine. Sorry, your browser doesn't support embedded videos.]]></summary></entry><entry><title type="html">Palette Swapping With Shaders</title><link href="https://pvigier.github.io/2019/10/06/palette-swapping-with-shaders.html" rel="alternate" type="text/html" title="Palette Swapping With Shaders" /><published>2019-10-06T00:00:00+02:00</published><updated>2019-10-06T00:00:00+02:00</updated><id>https://pvigier.github.io/2019/10/06/palette-swapping-with-shaders</id><content type="html" xml:base="https://pvigier.github.io/2019/10/06/palette-swapping-with-shaders.html"><![CDATA[<p>Hi everyone!</p>

<p>In this devlog, I will show you a technique that I love and that I will abuse in <a href="https://www.vagabondgame.com">Vagabond</a>: palette swapping.</p>

<p>Palette swapping is simply changing the palette of a texture. Here, we will do that at runtime using shaders. It was a useful technique in the old days to add variety in the assets without using too much memory. Now, it is used in procedural generation to produce new assets, I will show many examples in later devlogs.</p>

<p><img src="/media/img/palette-swapping-with-shaders/body_palette_swapping.gif" alt="" class="center-image modal-image" /></p>

<!--more-->

<h1 id="preparing-the-images">Preparing the Images</h1>

<p>The first step is to prepare your images for palette swapping. In a <a href="https://en.wikipedia.org/wiki/Raster_graphics">raster image</a>, each pixel contains a color. What we would like instead is that each pixel contains the index of its color in a palette. This way, we decouple the structure of the image (the areas with the same color) with the real colors.</p>

<p>In fact, several image formats support this way of storing images. For instance, the <a href="https://en.wikipedia.org/wiki/Portable_Network_Graphics#Pixel_format">PNG image format</a> has an indexed color option. Unfortunately, many libraries that load images will provide an array of colors even if the image was stored in indexed mode. It is the case of SFML, the library I used. It uses <a href="https://github.com/nothings/stb">stb_image</a> under the hood which automatically “depalettizes” images i.e. it replaces indices by the corresponding color in the palette.</p>

<p>Consequently, to avoid this problem, I store separately the image and the palette. The image is in grayscale mode and the gray level of each pixel corresponds to the index of its color in the palette.</p>

<p>Here is an example of what we expect:</p>

<p><img src="/media/img/palette-swapping-with-shaders/preprocess.png" alt="" class="center-image modal-image" /></p>

<p>To do that, I use a little Python function that uses the <a href="https://github.com/python-pillow/Pillow">Pillow</a> library:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">io</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>

<span class="k">def</span> <span class="nf">convert_to_indexed_image</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">palette_size</span><span class="p">):</span>
    <span class="c1"># Convert to an indexed image
</span>    <span class="n">indexed_image</span> <span class="o">=</span> <span class="n">image</span><span class="p">.</span><span class="n">convert</span><span class="p">(</span><span class="s">'RGBA'</span><span class="p">).</span><span class="n">convert</span><span class="p">(</span><span class="n">mode</span><span class="o">=</span><span class="s">'P'</span><span class="p">,</span> <span class="n">dither</span><span class="o">=</span><span class="s">'NONE'</span><span class="p">,</span> <span class="n">colors</span><span class="o">=</span><span class="n">palette_size</span><span class="p">)</span> <span class="c1"># Be careful it can remove colors
</span>    <span class="c1"># Save and load the image to update the info (transparency field in particular)
</span>    <span class="n">f</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">BytesIO</span><span class="p">()</span>
    <span class="n">indexed_image</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">'png'</span><span class="p">)</span>
    <span class="n">indexed_image</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
    <span class="c1"># Reinterpret the indexed image as a grayscale image
</span>    <span class="n">grayscale_image</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="n">fromarray</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">asarray</span><span class="p">(</span><span class="n">indexed_image</span><span class="p">),</span> <span class="s">'L'</span><span class="p">)</span>
    <span class="c1"># Create the palette
</span>    <span class="n">palette</span> <span class="o">=</span> <span class="n">indexed_image</span><span class="p">.</span><span class="n">getpalette</span><span class="p">()</span>
    <span class="n">transparency</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">indexed_image</span><span class="p">.</span><span class="n">info</span><span class="p">[</span><span class="s">'transparency'</span><span class="p">])</span>
    <span class="n">palette_colors</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">asarray</span><span class="p">([[</span><span class="n">palette</span><span class="p">[</span><span class="mi">3</span><span class="o">*</span><span class="n">i</span><span class="p">:</span><span class="mi">3</span><span class="o">*</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">transparency</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> \
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">palette_size</span><span class="p">)]]).</span><span class="n">astype</span><span class="p">(</span><span class="s">'uint8'</span><span class="p">)</span>
    <span class="n">palette_image</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="n">fromarray</span><span class="p">(</span><span class="n">palette_colors</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">'RGBA'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">grayscale_image</span><span class="p">,</span> <span class="n">palette_image</span>
</code></pre></div></div>

<p>Firstly, the function converts the image to the palette mode. Then, it reinterprets it as a grayscale image. Finally, it extracts the palette. Nothing fancy, all the hard work is done by Pillow.</p>

<h1 id="shader">Shader</h1>

<p>Now, that we have preprocessed our images, we are ready to write the shader to finally swap the palettes. There are two strategies to pass the palette to the shader: by using a texture or a uniform array. I find that it is easier to do it using a uniform array so I use that.</p>

<p>Here is my shader, I use GLSL but I think you can easily translate it in another shading language as it is dead simple:</p>

<div class="language-glsl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#version 330 core
</span><span class="k">in</span> <span class="kt">vec2</span> <span class="n">TexCoords</span><span class="p">;</span>

<span class="k">uniform</span> <span class="kt">sampler2D</span> <span class="n">Texture</span><span class="p">;</span>
<span class="k">uniform</span> <span class="kt">vec4</span> <span class="n">Palette</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>

<span class="k">out</span> <span class="kt">vec4</span> <span class="n">Color</span><span class="p">;</span>

<span class="kt">void</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Color</span> <span class="o">=</span> <span class="n">Palette</span><span class="p">[</span><span class="kt">int</span><span class="p">(</span><span class="n">texture</span><span class="p">(</span><span class="n">Texture</span><span class="p">,</span> <span class="n">TexCoords</span><span class="p">).</span><span class="n">r</span> <span class="o">*</span> <span class="mi">255</span><span class="p">)];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We just use the texture to read the red channel of the current texel. The red channel value is a floating-point number between 0 and 1 so we multiply by 255 and we cast to <code class="language-plaintext highlighter-rouge">int</code> to retrieve the original gray level between 0 and 255 that is stored in the image. Finally, we used that to get the color from the palette.</p>

<p>The animation at the beginning of the article comes from in-game screenshots where I use the following palettes to color the body of the character:</p>

<p><img src="/media/img/palette-swapping-with-shaders/body_palettes.png" alt="" class="center-image modal-image" /></p>

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

<p>That is all for palette swapping. I hope it gives you some ideas.</p>

<p>In a later post, I will show you how to procedurally generate palettes to push limits of palette swapping.</p>

<p>See you next week for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><category term="pcg" /><summary type="html"><![CDATA[Hi everyone! In this devlog, I will show you a technique that I love and that I will abuse in Vagabond: palette swapping. Palette swapping is simply changing the palette of a texture. Here, we will do that at runtime using shaders. It was a useful technique in the old days to add variety in the assets without using too much memory. Now, it is used in procedural generation to produce new assets, I will show many examples in later devlogs.]]></summary></entry><entry><title type="html">Vagabond – Exploring the World</title><link href="https://pvigier.github.io/2019/09/22/vagabond-exploring-the-world.html" rel="alternate" type="text/html" title="Vagabond – Exploring the World" /><published>2019-09-22T00:00:00+02:00</published><updated>2019-09-22T00:00:00+02:00</updated><id>https://pvigier.github.io/2019/09/22/vagabond-exploring-the-world</id><content type="html" xml:base="https://pvigier.github.io/2019/09/22/vagabond-exploring-the-world.html"><![CDATA[<p>Hi everyone!</p>

<p>This is the first devlog since I stopped working on the game engine and really started working on the game. I did not publish a post last week, I started writing an article about the new implementation of my <a href="https://github.com/pvigier/ecs">entity-component-system library</a> but I did not find the time to finish it yet. The last two weeks were pretty busy.</p>

<!--more-->

<h1 id="moving-in-the-world">Moving in the World</h1>

<p>The first goal was to be able to move a character in a generated world. To do that, I had to plug the <a href="/2019/05/26/vagabond-generating-tiles.html">world generator</a> with the rest of the game engine. Then, displaying the world and the character was just creating some nodes in the scene graph.</p>

<p>I tried to use the entity-component-system pattern as much as possible. It was a bit difficult at the beginning as it was the first time I used it in practice. Separating data in components and logic in systems is a manner of thinking I was not familiar with. However, it revealed extremely powerful, in particular in a roguelite and a multiplayer context. Being able to create new entities by blending components is useful to generate a huge amount of content. Moreover, the separation of logic in systems allows reusing common parts of logic between the server and the client easily.</p>

<p>Here is a small screencast of a (naked) character walking in the world:</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-exploring-the-world/walking.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

<h1 id="multiplayer-mode-is-working">Multiplayer Mode Is Working</h1>

<p>I decided to directly create the multiplayer mode. In fact, the single-player mode just runs a local server which does not accept any external connection. I think that in the long term, starting with this design is easier than firstly creating the single-player game and then trying to add a multiplayer mode.</p>

<p>The client does not generate nor store the world, it is streamed by the server. Thus, the connection is pretty fast, the player does not have to wait while the entire world is downloading.</p>

<p>Here is a small screencast of a second player joining the server opened by another player:</p>

<video controls="" width="600">
    <source src="/media/video/vagabond-exploring-the-world/multiplayer.mp4" type="video/mp4" />
    Sorry, your browser doesn't support embedded videos.
</video>

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

<p>The next goal is to be able to add pieces of equipment to the characters so that they are not naked anymore!</p>

<p>See you next week for more!</p>]]></content><author><name>pierre</name></author><category term="vagabond" /><summary type="html"><![CDATA[Hi everyone! This is the first devlog since I stopped working on the game engine and really started working on the game. I did not publish a post last week, I started writing an article about the new implementation of my entity-component-system library but I did not find the time to finish it yet. The last two weeks were pretty busy.]]></summary></entry></feed>