<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-07-06T13:35:04+00:00</updated><id>/feed.xml</id><title type="html">Lucy’s Blog</title><subtitle>Lucy&apos;s Blog where I talk about programming and I show off my projects. I might also talk about other stuff.
</subtitle><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><entry><title type="html">Lucy’s Direct3D 12 Renderer - Devlog #1</title><link href="/blog/lucydx12-devlog-1.html" rel="alternate" type="text/html" title="Lucy’s Direct3D 12 Renderer - Devlog #1" /><published>2026-07-04T00:00:00+00:00</published><updated>2026-07-04T00:00:00+00:00</updated><id>/blog/lucydx12-devlog-1</id><content type="html" xml:base="/blog/lucydx12-devlog-1.html"><![CDATA[<p><img src="../assets/lucydx12/lucydx12_wSHvcXNoc5.jpg" alt="lucydx12" /></p>

<p>I’ve been working on Direct3D 12 for over half a year and I thought it was about time to write about what I’ve learned. Here’s some cool stuff I’ve learned in the process. But before that, an overview:</p>

<p><a href="https://github.com/lucypero/lucydx12">Link to the project’s source</a></p>

<h1 id="what-is-this-project-about">What is this project about?</h1>

<p>It just started as me trying to learn Direct3D 12 (DX12 for short). The goal is not defined well yet. For now, I’m just doing stuff I find interesting in the 3D rendering realm, and things that seem to be essential for a 3D renderer and/or game engine. Also, I’ll do stuff that results in pretty frames, of course.</p>

<p>In the future, This project might turn into a real product, like a 3D renderer or game engine that would actually be useful for some end. But for now, it’s just me learning stuff and having fun.</p>

<h2 id="how-did-i-learn-all-this">How did I learn all this?</h2>

<p>In short: Google, Gemini, and smart and kind people answering my questions in Discord. Plus, a little bit from books. I link to articles further into this article.</p>

<ul>
  <li>(book) <a href="https://www.amazon.com/dp/1138627003?lv=shuf&amp;channelId=500&amp;plpRedirect=mhFallback">Real Time Rendering</a> for rendering techniques.</li>
  <li>(book) <a href="https://www.amazon.com/Engine-Architecture-Third-Jason-Gregory/dp/1138035459?crid=1HY8KOV9OQAH9&amp;dib=eyJ2IjoiMSJ9.C1WfWFj2hVuDjMPJ_9fwoxoDnnoxssV1RZ-lt_weN3iLqetMclO1bkfgZFN39WJu8eyDtal5IDYZ4HeqfvXZcyhim-8KjmQcOxJdnJgT2buaO0qkYv0aiJ2l-gEugiYPv6Hvbwpgamo7w4S5B8af3gOoPlhSHOuTQNciwts-AhQyw6K5oyYHZZcyXGGb_qSPbZtTn9T8Hu--BxbvcgSdu7BJy39LqoqiNorTziF-d8w.ydDsxjq6lffURL1kYEuCCqtwRLrGnLTWJPcBwIQeN0E&amp;dib_tag=se&amp;keywords=Game+Engine+Architecture&amp;qid=1783271361&amp;s=books&amp;sprefix=%2Cstripbooks%2C242&amp;sr=1-2">Game Engine Architecture</a> for multithreading theory.</li>
</ul>

<h1 id="tools-used">Tools used</h1>

<ul>
  <li><a href="https://odin-lang.org/">Odin</a>: The greatest programming language.</li>
  <li><a href="https://zed.dev/">Zed</a>: It’s currently my favorite code editor. It’s awesome.</li>
  <li><a href="https://github.com/EpicGames/raddebugger">The RAD Debugger</a>: The greatest debugger ever made. It’s still in alpha so there are some bugs, but still it’s an absolute joy to use.</li>
  <li><a href="https://renderdoc.org/">RenderDoc</a>: Essential for debugging the GPU. I love it. Recently I even set up shader debugging. I can debug shader code as if it’s CPU code, which is really cool.</li>
  <li><a href="https://github.com/colrdavidson/spall-web">Spall</a>: I don’t use this anymore as I found it limiting; and development has ceased. In the future, I’ll try another profiler, like Tracy.</li>
  <li>Git + <a href="https://www.sublimemerge.com/">Sublime Merge</a>: As I’ve said before, Sublime Merge is the best way to interact with Git, and everyone should use it. I’ve used it for many years, and I forced all the artists at work to use it. And they’re happy for it.</li>
  <li>And of course, I have to use Windows for DX12. But that’s OK, as all the best software runs on it (and <em>only</em> on it in some instances like RAD), and it’s still the <a href="https://lucypero.com/blog/windows-development.html">best OS for software development</a>.</li>
</ul>

<h1 id="learning-dx12-itself-and-coding-the-project">Learning DX12 itself, and coding the project</h1>

<p>Coming from DX11, DX12 is big. You have to manually do some things that I took for granted. A lot of it is boring so I won’t write much about it. The great thing is: DX12 itself has changed a lot over the years. It is now over 10 years old. It improved a lot over the years, and it got simpler in many ways. A lot of this is thanks to how much the hardware has changed, as well. <a href="https://www.sebastianaaltonen.com/blog/no-graphics-api">Sebastian Aaltonen</a> talks about this.</p>

<p>What’s great is that right now, you don’t have to do many things that you had to do before in older versions of DX12, and in older graphics API’s like DX11 and OpenGL.</p>

<ul>
  <li>One big thing is the whole concept of “bindless”: If your hardware supports this, you no longer have to “bind” specific resources on your Root signature when executing a PSO (Pipeline State Object). This means that you can have one single and very simple Root Signature for all your PSO’s, and access every resource you need from your shader by indexing directly from your Resource Heap. So now, all you need to sample a texture in a shader now is to create the texture resource and SRV, pass the index of the texture to the shader in any way you want (I pass indexes mostly through one big CBV), and that’s it. No more rigid binding of resources that resist refactors. This keeps both CPU and GPU code lean and fast.</li>
</ul>

<p>I notice as I’m writing this that this is extremely technical and thus very hard to explain. I’ll link this <a href="https://rtarun9.github.io/blogs/bindless_rendering/">article</a> that talks about bindless rendering better than I can.</p>

<ul>
  <li>You can go even further with this concept and do away with Input Layouts all together, but I haven’t done that (yet). That goes too far for me just yet. Also there’s some possible bad performance implications, as this <a href="https://www.yosoygames.com.ar/wp/2018/03/vertex-formats-part-2-fetch-vs-pull/">article</a> explains.</li>
</ul>

<p>As I’ve been writing this renderer, I took some time doing reformatting. Turns out, dealing with DX12 isn’t so bad when you understand it well enough, and have the right structs and helper structs. <code class="language-plaintext highlighter-rouge">texture_create</code> is doing a <a href="https://github.com/lucypero/lucydx12/blob/main/src/dx_helpers.odin#L494">looot of things in my code</a>. Tying together the texture’s resource with its views is essential, as seen in my <code class="language-plaintext highlighter-rouge">Texture</code> struct.</p>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// indexes are -1 if the view was not created</span>
<span class="n">Texture</span> <span class="p">::</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">buffer</span><span class="p">:</span> <span class="o">^</span><span class="n">dx</span><span class="p">.</span><span class="n">IResource</span><span class="p">,</span>
	<span class="n">format</span><span class="p">:</span> <span class="n">dxgi</span><span class="p">.</span><span class="n">FORMAT</span><span class="p">,</span>
	<span class="n">srv_index</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span>
	<span class="n">dsv_index</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span>
	<span class="n">rtv_index</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span>
	<span class="n">uav_index</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span>
	<span class="n">width</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span>
	<span class="n">height</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I learned pretty early that it’s good to create one “Uber heap” per each heap type in DX12 and just put every descriptor in one of those as you create them - see <code class="language-plaintext highlighter-rouge">uber_heap_create</code> <a href="https://github.com/lucypero/lucydx12/blob/main/src/dx_helpers.odin#L38C1-L38C17">here</a></p>

<p>One other thing of note is how I’m handling PSO’s (Pipeline State Objects). I just have one <code class="language-plaintext highlighter-rouge">pso_create</code> that defines <em>everything</em> about a <a href="https://github.com/lucypero/lucydx12/blob/main/src/dx_helpers.odin#L229">PSO</a>. The render proc that I pass as a proc pointer then gets called automatically every frame. The renderer renders all PSO’s in an array, in order, automatically:</p>

<p>Here, I’m using Odin’s <a href="https://odin-lang.org/docs/overview/#enumerated-array">enumerated arrays</a>.</p>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// This gets stored in a huge Context struct that holds everything</span>
<span class="n">psos</span><span class="p">:</span> <span class="p">[</span><span class="n">PSOName</span><span class="p">]</span><span class="n">PSO</span><span class="p">,</span>
</code></pre></div></div>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// All our PSO's</span>
<span class="c1">// The ORDER of these items matter a lot. it's the order in which the PSO's are rendered.</span>
<span class="n">PSOName</span> <span class="p">::</span> <span class="k">enum</span> <span class="p">{</span>
	<span class="n">Shadowmap</span><span class="p">,</span>
	<span class="n">GBuffer_Pass</span><span class="p">,</span>
	<span class="n">Lighting_Pass</span><span class="p">,</span>
	<span class="n">Gizmos</span><span class="p">,</span>
	<span class="n">PostProcess</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// in the render loop..</span>
<span class="k">for</span> <span class="n">pso</span> <span class="k">in</span> <span class="n">ct</span><span class="p">.</span><span class="n">psos</span> <span class="p">{</span>
	<span class="n">pso</span><span class="p">.</span><span class="nf">render_proc</span><span class="p">(</span><span class="n">pso</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="deferred-rendering">Deferred rendering</h1>

<p>The renderer uses a standard deferred rendering setup: I first do a geometry pass that renders to 3 Buffers: Albedo, Normal, and AO+Roughness. Then, a lighting pass reads from these buffers to produce a frame. The pipeline keeps going after that, but that’s the deferred part.</p>

<p><img src="../assets/lucydx12/qrenderdoc_Su2VCUDu8B.png" alt="lucydx12" /></p>

<h1 id="worker-thread-for-asset-loading">Worker thread for asset loading</h1>

<p>Turns out the GPU has a whole separate pipeline just for copying data from CPU to GPU memory. So I set that up, mostly in <a href="https://github.com/lucypero/lucydx12/blob/main/src/dx_upload.odin">here</a>. I have a worker thread that waits for scenes to be in a “to load” state, and starts loading the assets from disk to GPU memory. The threading code is probably very bad and there’s probably some race conditions in there. I used atomics to synchronize things. I learned a lot about multithreading while doing this. It’s still all very bad but it seems to work. You can now switch between scenes without any stutters, as assets get loaded to the GPU in the background without interrupting rendering.</p>

<p>This video shows a scene transition, as the camera is moving. No stutters. Butter-smooth gameplay.</p>

<video controls="">
  <source src="../assets/lucydx12/lucydx12_1ydJcmnS2Q.mp4" type="video/mp4" />
</video>

<h1 id="anti-aliasing-without-msaa">Anti Aliasing without MSAA</h1>

<p>I’m not doing multiple samples per pixel because I’m on a deferred rendering setup. Therefore, Anti-Aliasing becomes tricky. For now, I set up a compute pass at the end of the pipeline, right before drawing the dear-imgui interface. And in there, I do <a href="https://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf">FXAA</a>. The results are OK.</p>

<p><img src="../assets/lucydx12/firefox_0LsaA8A3MY.png" alt="lucydx12" /></p>

<p>There was a hiccup while integrating FXAA. I was passing it the wrong UV coordinates. Instead of passing the center of the pixels, I was passing the corner. This resulted in a blurry image, with no evidence of edge detection.</p>

<p>Here’s the right way to pass UV’s to FXAA, in a compute shader:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">float2</span> <span class="n">inverse_screen_size</span> <span class="o">=</span> <span class="n">float2</span><span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="kt">float</span><span class="p">(</span><span class="n">width</span><span class="p">),</span> <span class="mi">1</span> <span class="o">/</span> <span class="kt">float</span><span class="p">(</span><span class="n">height</span><span class="p">));</span>
<span class="c1">// adding offset to land on the center of the pixel</span>
<span class="n">float2</span> <span class="n">pixel_pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">float2</span><span class="p">(</span><span class="n">dispatchThreadID</span><span class="p">.</span><span class="n">xy</span><span class="p">)</span> <span class="o">+</span> <span class="mi">0</span><span class="p">.</span><span class="mi">5</span><span class="n">f</span><span class="p">)</span> <span class="o">*</span> <span class="n">inverse_screen_size</span><span class="p">;</span>

<span class="c1">// later...</span>

<span class="n">FxaaFloat4</span> <span class="n">fxaa_out</span> <span class="o">=</span> <span class="n">FxaaPixelShader</span><span class="p">(</span>
	<span class="n">pixel_pos</span><span class="p">,</span> <span class="c1">// pos</span>

<span class="c1">// ...</span>
<span class="p">);</span>

</code></pre></div></div>

<p>A lot more could be done in regards to AA. It’s a big field in computer graphics. I will probably experiment with more techniques in the future.</p>

<h1 id="shadow-maps">Shadow maps</h1>

<p>I’ve implemented basic shadowmapping with PCF filtering. Not much to say here. I did the bare minimum. The shadowmap projection view’s parameters are manually picked by me until the frame looked good. Much more work is needed here.</p>

<video controls="">
  <source src="../assets/lucydx12/lucydx12_4ECcmMXWgj.mp4" type="video/mp4" />
</video>

<h1 id="using-odins-runtime-type-information-rtti-for-shader-code">Using Odin’s Runtime Type Information (RTTI) for shader code</h1>

<p>I’ve used Odin’s <a href="https://pkg.odin-lang.org/core/reflect/">RTTI</a> to automate some things related to shaders. Odin packs information about your structs and other things in the executable, which let you automate some things very easily:</p>

<h2 id="input-layouts">Input Layouts</h2>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ct</span><span class="p">.</span><span class="n">psos</span><span class="p">[.</span><span class="n">Shadowmap</span><span class="p">]</span> <span class="o">=</span> <span class="nf">pso_create</span><span class="p">(</span><span class="s">"src/shaders/shadowmap.hlsl"</span><span class="p">,</span> <span class="n">PSOParameters</span> <span class="p">{</span>
	<span class="n">vertex_input</span> <span class="o">=</span> <span class="n">VertexData</span><span class="p">,</span> <span class="c1">// Passing a struct as the vertex input</span>
	<span class="n">blend_state</span> <span class="o">=</span> <span class="p">.</span><span class="n">Off</span><span class="p">,</span>
	<span class="n">enable_depth</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
	<span class="n">depth_write</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
	<span class="n">rtv_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="p">},</span> <span class="n">render_proc</span> <span class="o">=</span> <span class="n">pso_shadowmap_render</span><span class="p">,</span> <span class="n">pso_name</span> <span class="o">=</span> <span class="s">"Shadowmap pso"</span><span class="p">)</span>
</code></pre></div></div>

<p>in <code class="language-plaintext highlighter-rouge">pso_create</code>, I simply pass a struct name, and the code dynamically generates the Input Layout for the PSO out of a struct definition. Defining Input Layouts and keeping them in sync with your structs was one of the most annoying parts of a graphics API, and now it’s abstracted away. So, this struct:</p>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">VertexData</span> <span class="p">::</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">pos</span><span class="p">:</span> <span class="n">v3</span> <span class="s">`POSITION`</span><span class="p">,</span>
	<span class="n">normal</span><span class="p">:</span> <span class="n">v3</span> <span class="s">`NORMAL`</span><span class="p">,</span>
	<span class="n">tangent</span><span class="p">:</span> <span class="n">v4</span> <span class="s">`TANGENT`</span><span class="p">,</span>
	<span class="n">uv</span><span class="p">:</span> <span class="n">v2</span> <span class="s">`TEXCOORD`</span><span class="p">,</span>
	<span class="n">uv_2</span><span class="p">:</span> <span class="n">v2</span> <span class="s">`TEXCOORD_SECOND_UV`</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>… gets auto-converted into this:</p>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="n">INPUT_ELEMENT_DESC</span><span class="p">{</span><span class="n">SemanticName</span> <span class="o">=</span> <span class="s">"POSITION"</span><span class="p">,</span> <span class="n">SemanticIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">Format</span> <span class="o">=</span> <span class="s">"R32G32B32_FLOAT"</span><span class="p">,</span> <span class="n">InputSlot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">AlignedByteOffset</span> <span class="o">=</span> <span class="mi">4294967295</span><span class="p">,</span> <span class="n">InputSlotClass</span> <span class="o">=</span> <span class="s">"PER_VERTEX_DATA"</span><span class="p">,</span> <span class="n">InstanceDataStepRate</span> <span class="o">=</span> <span class="mi">0</span><span class="p">}</span>
  <span class="n">INPUT_ELEMENT_DESC</span><span class="p">{</span><span class="n">SemanticName</span> <span class="o">=</span> <span class="s">"NORMAL"</span><span class="p">,</span> <span class="n">SemanticIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">Format</span> <span class="o">=</span> <span class="s">"R32G32B32_FLOAT"</span><span class="p">,</span> <span class="n">InputSlot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">AlignedByteOffset</span> <span class="o">=</span> <span class="mi">4294967295</span><span class="p">,</span> <span class="n">InputSlotClass</span> <span class="o">=</span> <span class="s">"PER_VERTEX_DATA"</span><span class="p">,</span> <span class="n">InstanceDataStepRate</span> <span class="o">=</span> <span class="mi">0</span><span class="p">}</span>
  <span class="n">INPUT_ELEMENT_DESC</span><span class="p">{</span><span class="n">SemanticName</span> <span class="o">=</span> <span class="s">"TANGENT"</span><span class="p">,</span> <span class="n">SemanticIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">Format</span> <span class="o">=</span> <span class="s">"R32G32B32A32_FLOAT"</span><span class="p">,</span> <span class="n">InputSlot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">AlignedByteOffset</span> <span class="o">=</span> <span class="mi">4294967295</span><span class="p">,</span> <span class="n">InputSlotClass</span> <span class="o">=</span> <span class="s">"PER_VERTEX_DATA"</span><span class="p">,</span> <span class="n">InstanceDataStepRate</span> <span class="o">=</span> <span class="mi">0</span><span class="p">}</span>
  <span class="n">INPUT_ELEMENT_DESC</span><span class="p">{</span><span class="n">SemanticName</span> <span class="o">=</span> <span class="s">"TEXCOORD"</span><span class="p">,</span> <span class="n">SemanticIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">Format</span> <span class="o">=</span> <span class="s">"R32G32_FLOAT"</span><span class="p">,</span> <span class="n">InputSlot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">AlignedByteOffset</span> <span class="o">=</span> <span class="mi">4294967295</span><span class="p">,</span> <span class="n">InputSlotClass</span> <span class="o">=</span> <span class="s">"PER_VERTEX_DATA"</span><span class="p">,</span> <span class="n">InstanceDataStepRate</span> <span class="o">=</span> <span class="mi">0</span><span class="p">}</span>
  <span class="n">INPUT_ELEMENT_DESC</span><span class="p">{</span><span class="n">SemanticName</span> <span class="o">=</span> <span class="s">"TEXCOORD_SECOND_UV"</span><span class="p">,</span> <span class="n">SemanticIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">Format</span> <span class="o">=</span> <span class="s">"R32G32_FLOAT"</span><span class="p">,</span> <span class="n">InputSlot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">AlignedByteOffset</span> <span class="o">=</span> <span class="mi">4294967295</span><span class="p">,</span> <span class="n">InputSlotClass</span> <span class="o">=</span> <span class="s">"PER_VERTEX_DATA"</span><span class="p">,</span> <span class="n">InstanceDataStepRate</span> <span class="o">=</span> <span class="mi">0</span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>… Which is passed to DX12 in a call to <code class="language-plaintext highlighter-rouge">CreateGraphicsPipelineState()</code>.</p>

<h2 id="generating-hlsl-structs-out-of-odin-structs">Generating HLSL structs out of Odin structs</h2>

<p>In a similar way, I use RTTI to generate structs on the HLSL side. Example:</p>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1">// This is a list of normal Odin structs.</span>

<span class="nd">@(rodata)</span>
<span class="n">TYPES_FOR_HLSL</span> <span class="o">:=</span> <span class="p">[</span><span class="o">?</span><span class="p">]</span><span class="k">typeid</span><span class="p">{</span><span class="n">GeneralConstants</span><span class="p">,</span> <span class="n">DrawConstants</span><span class="p">,</span> <span class="n">LightType</span><span class="p">,</span> <span class="n">Light</span><span class="p">,</span> <span class="n">TextureUV</span><span class="p">,</span> <span class="n">Material</span><span class="p">}</span>

<span class="c1">// For example, Light:</span>

<span class="n">Light</span> <span class="p">::</span> <span class="k">struct</span> <span class="cp">#align</span> <span class="p">(</span><span class="mi">16</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">type</span><span class="p">:</span> <span class="n">LightType</span><span class="p">,</span>
	<span class="n">position</span><span class="p">:</span> <span class="n">v3</span><span class="p">,</span>

	<span class="n">radius</span><span class="p">:</span> <span class="kt">f32</span><span class="p">,</span>
	<span class="n">direction</span><span class="p">:</span> <span class="n">v3</span><span class="p">,</span>

	<span class="n">intensity</span><span class="p">:</span> <span class="kt">f32</span><span class="p">,</span>
	<span class="n">color</span><span class="p">:</span> <span class="n">v3</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// and then we generate like this...</span>

<span class="c1">// ...</span>

<span class="k">for</span> <span class="n">type</span> <span class="k">in</span> <span class="n">TYPES_FOR_HLSL</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="p">.</span><span class="nf">sbprintfln</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sb</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">%v"</span><span class="p">,</span> <span class="nf">convert_struct_odin_to_hlsl</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="nb">context</span><span class="p">.</span><span class="n">temp_allocator</span><span class="p">))</span>
<span class="p">}</span>

</code></pre></div></div>

<p>The output for <code class="language-plaintext highlighter-rouge">Light</code> in HLSL becomes:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Light</span> <span class="p">{</span>
	<span class="n">LightType</span> <span class="n">type</span><span class="p">;</span>
	<span class="n">float3</span> <span class="n">position</span><span class="p">;</span>
	<span class="kt">float</span> <span class="n">radius</span><span class="p">;</span>
	<span class="n">float3</span> <span class="n">direction</span><span class="p">;</span>
	<span class="kt">float</span> <span class="n">intensity</span><span class="p">;</span>
	<span class="n">float3</span> <span class="n">color</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The best thing about generating this code is that it only took me a few hours to learn and set up.</p>

<h1 id="gltf-processing">GLTF processing</h1>

<p>I get all asset data from a <code class="language-plaintext highlighter-rouge">.gltf</code> file. One interesting thing I did was a texture cache. GLTF doesn’t store textures in a GPU optimized format, so what I did is this: when the code reaches the point where it’s time to load a texture from the GLTF file, it consults a file cache to see if I already converted the texture. If I didn’t, it generates the texture right there, with mipmapping. It does take a while to generate the textures. I use <code class="language-plaintext highlighter-rouge">texconv.exe</code> for this.</p>

<h1 id="other-stuff">Other stuff:</h1>

<ul>
  <li><a href="https://github.com/ocornut/imgui">dear-imgui</a> integration: I love this library! Using it, coupled with Odin’s RTTI, I can tweak whatever I want in the program visually so, so quickly.
    <ul>
      <li>Used <a href="https://gitlab.com/L-4/odin-imgui">Odin Imgui</a> for dear-imgui bindings for Odin.</li>
    </ul>
  </li>
</ul>

<h1 id="what-to-work-on-next">What to work on next</h1>

<p>This renderer is only getting started. It still doesn’t even have a proper lighting model. Lots of work is still to be done. And I’m still not sure what to make of this project. But as long as I’m learning cool things and I have fun, I will continue. I hope you got something out of this article.</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="programming" /><category term="odin" /><summary type="html"><![CDATA[I’ve been working on Direct3D 12 for over half a year and I thought it was about time to write about what I’ve learned. Here’s some cool stuff I’ve learned in the process. But before that, an overview: Link to the project’s source What is this project about? It just started as me trying to learn Direct3D 12 (DX12 for short). The goal is not defined well yet. For now, I’m just doing stuff I find interesting in the 3D rendering realm, and things that seem to be essential for a 3D renderer and/or game engine. Also, I’ll do stuff that results in pretty frames, of course. In the future, This project might turn into a real product, like a 3D renderer or game engine that would actually be useful for some end. But for now, it’s just me learning stuff and having fun. How did I learn all this? In short: Google, Gemini, and smart and kind people answering my questions in Discord. Plus, a little bit from books. I link to articles further into this article. (book) Real Time Rendering for rendering techniques. (book) Game Engine Architecture for multithreading theory. Tools used Odin: The greatest programming language. Zed: It’s currently my favorite code editor. It’s awesome. The RAD Debugger: The greatest debugger ever made. It’s still in alpha so there are some bugs, but still it’s an absolute joy to use. RenderDoc: Essential for debugging the GPU. I love it. Recently I even set up shader debugging. I can debug shader code as if it’s CPU code, which is really cool. Spall: I don’t use this anymore as I found it limiting; and development has ceased. In the future, I’ll try another profiler, like Tracy. Git + Sublime Merge: As I’ve said before, Sublime Merge is the best way to interact with Git, and everyone should use it. I’ve used it for many years, and I forced all the artists at work to use it. And they’re happy for it. And of course, I have to use Windows for DX12. But that’s OK, as all the best software runs on it (and only on it in some instances like RAD), and it’s still the best OS for software development. Learning DX12 itself, and coding the project Coming from DX11, DX12 is big. You have to manually do some things that I took for granted. A lot of it is boring so I won’t write much about it. The great thing is: DX12 itself has changed a lot over the years. It is now over 10 years old. It improved a lot over the years, and it got simpler in many ways. A lot of this is thanks to how much the hardware has changed, as well. Sebastian Aaltonen talks about this. What’s great is that right now, you don’t have to do many things that you had to do before in older versions of DX12, and in older graphics API’s like DX11 and OpenGL. One big thing is the whole concept of “bindless”: If your hardware supports this, you no longer have to “bind” specific resources on your Root signature when executing a PSO (Pipeline State Object). This means that you can have one single and very simple Root Signature for all your PSO’s, and access every resource you need from your shader by indexing directly from your Resource Heap. So now, all you need to sample a texture in a shader now is to create the texture resource and SRV, pass the index of the texture to the shader in any way you want (I pass indexes mostly through one big CBV), and that’s it. No more rigid binding of resources that resist refactors. This keeps both CPU and GPU code lean and fast. I notice as I’m writing this that this is extremely technical and thus very hard to explain. I’ll link this article that talks about bindless rendering better than I can. You can go even further with this concept and do away with Input Layouts all together, but I haven’t done that (yet). That goes too far for me just yet. Also there’s some possible bad performance implications, as this article explains. As I’ve been writing this renderer, I took some time doing reformatting. Turns out, dealing with DX12 isn’t so bad when you understand it well enough, and have the right structs and helper structs. texture_create is doing a looot of things in my code. Tying together the texture’s resource with its views is essential, as seen in my Texture struct. // indexes are -1 if the view was not created Texture :: struct { buffer: ^dx.IResource, format: dxgi.FORMAT, srv_index: int, dsv_index: int, rtv_index: int, uav_index: int, width: int, height: int, } I learned pretty early that it’s good to create one “Uber heap” per each heap type in DX12 and just put every descriptor in one of those as you create them - see uber_heap_create here One other thing of note is how I’m handling PSO’s (Pipeline State Objects). I just have one pso_create that defines everything about a PSO. The render proc that I pass as a proc pointer then gets called automatically every frame. The renderer renders all PSO’s in an array, in order, automatically: Here, I’m using Odin’s enumerated arrays. /// This gets stored in a huge Context struct that holds everything psos: [PSOName]PSO, // All our PSO's // The ORDER of these items matter a lot. it's the order in which the PSO's are rendered. PSOName :: enum { Shadowmap, GBuffer_Pass, Lighting_Pass, Gizmos, PostProcess } // in the render loop.. for pso in ct.psos { pso.render_proc(pso) } Deferred rendering The renderer uses a standard deferred rendering setup: I first do a geometry pass that renders to 3 Buffers: Albedo, Normal, and AO+Roughness. Then, a lighting pass reads from these buffers to produce a frame. The pipeline keeps going after that, but that’s the deferred part. Worker thread for asset loading Turns out the GPU has a whole separate pipeline just for copying data from CPU to GPU memory. So I set that up, mostly in here. I have a worker thread that waits for scenes to be in a “to load” state, and starts loading the assets from disk to GPU memory. The threading code is probably very bad and there’s probably some race conditions in there. I used atomics to synchronize things. I learned a lot about multithreading while doing this. It’s still all very bad but it seems to work. You can now switch between scenes without any stutters, as assets get loaded to the GPU in the background without interrupting rendering. This video shows a scene transition, as the camera is moving. No stutters. Butter-smooth gameplay. Anti Aliasing without MSAA I’m not doing multiple samples per pixel because I’m on a deferred rendering setup. Therefore, Anti-Aliasing becomes tricky. For now, I set up a compute pass at the end of the pipeline, right before drawing the dear-imgui interface. And in there, I do FXAA. The results are OK. There was a hiccup while integrating FXAA. I was passing it the wrong UV coordinates. Instead of passing the center of the pixels, I was passing the corner. This resulted in a blurry image, with no evidence of edge detection. Here’s the right way to pass UV’s to FXAA, in a compute shader: float2 inverse_screen_size = float2(1 / float(width), 1 / float(height)); // adding offset to land on the center of the pixel float2 pixel_pos = (float2(dispatchThreadID.xy) + 0.5f) * inverse_screen_size; // later... FxaaFloat4 fxaa_out = FxaaPixelShader( pixel_pos, // pos // ... ); A lot more could be done in regards to AA. It’s a big field in computer graphics. I will probably experiment with more techniques in the future. Shadow maps I’ve implemented basic shadowmapping with PCF filtering. Not much to say here. I did the bare minimum. The shadowmap projection view’s parameters are manually picked by me until the frame looked good. Much more work is needed here. Using Odin’s Runtime Type Information (RTTI) for shader code I’ve used Odin’s RTTI to automate some things related to shaders. Odin packs information about your structs and other things in the executable, which let you automate some things very easily: Input Layouts ct.psos[.Shadowmap] = pso_create("src/shaders/shadowmap.hlsl", PSOParameters { vertex_input = VertexData, // Passing a struct as the vertex input blend_state = .Off, enable_depth = true, depth_write = true, rtv_count = 0, }, render_proc = pso_shadowmap_render, pso_name = "Shadowmap pso") in pso_create, I simply pass a struct name, and the code dynamically generates the Input Layout for the PSO out of a struct definition. Defining Input Layouts and keeping them in sync with your structs was one of the most annoying parts of a graphics API, and now it’s abstracted away. So, this struct: VertexData :: struct { pos: v3 `POSITION`, normal: v3 `NORMAL`, tangent: v4 `TANGENT`, uv: v2 `TEXCOORD`, uv_2: v2 `TEXCOORD_SECOND_UV`, } … gets auto-converted into this: { INPUT_ELEMENT_DESC{SemanticName = "POSITION", SemanticIndex = 0, Format = "R32G32B32_FLOAT", InputSlot = 0, AlignedByteOffset = 4294967295, InputSlotClass = "PER_VERTEX_DATA", InstanceDataStepRate = 0} INPUT_ELEMENT_DESC{SemanticName = "NORMAL", SemanticIndex = 0, Format = "R32G32B32_FLOAT", InputSlot = 0, AlignedByteOffset = 4294967295, InputSlotClass = "PER_VERTEX_DATA", InstanceDataStepRate = 0} INPUT_ELEMENT_DESC{SemanticName = "TANGENT", SemanticIndex = 0, Format = "R32G32B32A32_FLOAT", InputSlot = 0, AlignedByteOffset = 4294967295, InputSlotClass = "PER_VERTEX_DATA", InstanceDataStepRate = 0} INPUT_ELEMENT_DESC{SemanticName = "TEXCOORD", SemanticIndex = 0, Format = "R32G32_FLOAT", InputSlot = 0, AlignedByteOffset = 4294967295, InputSlotClass = "PER_VERTEX_DATA", InstanceDataStepRate = 0} INPUT_ELEMENT_DESC{SemanticName = "TEXCOORD_SECOND_UV", SemanticIndex = 0, Format = "R32G32_FLOAT", InputSlot = 0, AlignedByteOffset = 4294967295, InputSlotClass = "PER_VERTEX_DATA", InstanceDataStepRate = 0} } … Which is passed to DX12 in a call to CreateGraphicsPipelineState(). Generating HLSL structs out of Odin structs In a similar way, I use RTTI to generate structs on the HLSL side. Example: // This is a list of normal Odin structs. @(rodata) TYPES_FOR_HLSL := [?]typeid{GeneralConstants, DrawConstants, LightType, Light, TextureUV, Material} // For example, Light: Light :: struct #align (16) { type: LightType, position: v3, radius: f32, direction: v3, intensity: f32, color: v3, } // and then we generate like this... // ... for type in TYPES_FOR_HLSL { fmt.sbprintfln(&amp;sb, "\n%v", convert_struct_odin_to_hlsl(type, context.temp_allocator)) } The output for Light in HLSL becomes: struct Light { LightType type; float3 position; float radius; float3 direction; float intensity; float3 color; }; The best thing about generating this code is that it only took me a few hours to learn and set up. GLTF processing I get all asset data from a .gltf file. One interesting thing I did was a texture cache. GLTF doesn’t store textures in a GPU optimized format, so what I did is this: when the code reaches the point where it’s time to load a texture from the GLTF file, it consults a file cache to see if I already converted the texture. If I didn’t, it generates the texture right there, with mipmapping. It does take a while to generate the textures. I use texconv.exe for this. Other stuff: dear-imgui integration: I love this library! Using it, coupled with Odin’s RTTI, I can tweak whatever I want in the program visually so, so quickly. Used Odin Imgui for dear-imgui bindings for Odin. What to work on next This renderer is only getting started. It still doesn’t even have a proper lighting model. Lots of work is still to be done. And I’m still not sure what to make of this project. But as long as I’m learning cool things and I have fun, I will continue. I hope you got something out of this article.]]></summary></entry><entry><title type="html">Final Fantasy 16’s combat is bad, actually</title><link href="/blog/final-fantasy-16-bad-combat.html" rel="alternate" type="text/html" title="Final Fantasy 16’s combat is bad, actually" /><published>2026-04-27T00:00:00+00:00</published><updated>2026-04-27T00:00:00+00:00</updated><id>/blog/final-fantasy-16-combat</id><content type="html" xml:base="/blog/final-fantasy-16-bad-combat.html"><![CDATA[<p>I’ve seen people praise the combat of this game, so I had to speak up. I’ve even seen people who didn’t like the game overall say that the combat is “solid” and the strongest aspect of the game. I disagree.</p>

<p>This game is a mixed bag. Even after starting to play it months ago, having beaten it, plus the DLC (I regret buying that), and having played it for a total of about 40 hours perhaps, I’m still not sure what to think of this game.</p>

<p>It has high highs but also low lows. The visual spectacle on some sequences are the best gaming has to offer. The political intrigue, the worldbuilding, and the plot are excellent. The Active Lore System, or whatever they call it, is very innovative and serves a real need the game has. There is so much good about this game. But there’s also a lot of bad: Most characters are bland. The game has a lot of filler. Sometimes the game feels like a boring MMO. Some gameplay systems are <em>extremely</em> shallow, like the crafting system, and the gear system in general. It is such a strange game to critique.</p>

<p>The game also has a huge problem with difficulty: Throughout the vast majority of the game, I didn’t feel challenged in any way. It seems like they streamlined the game for a broad audience a little bit <em>too much</em>. The game has many accessibility options, which I think it’s great, but it only goes one way: It provides accessibility to the casual gamer. But if you are someone who requires to be mechanically challenged in a game to stay engaged, there’s <em>nothing</em> for you. They only have one hard difficulty mode, but you have to first beat the game to unlock it. Which makes it useless. There’s no way in <strong>hell</strong> I’m playing this game again. Are you serious? They seriously have to stop locking difficulty modes like this.</p>

<p>There isn’t a single gameplay system in the game with any kind of decent depth: The crafting system. The Gear system. The combat build system. The dog system. It’s all… empty. Like they were legally required to add these systems and that’s it. There’s nothing to them! Seriously, you literally will just pick the sword with the <em>single</em> stat that is higher (Swords have <em>two</em> stats, but they are always about the same value anyway), and that’s basically it! It’s an absolute joke. There is basically no RPG mechanics in this game. None. You can give commands to your dog, but it’s all filler and useless. The dog won’t ever make a difference in a battle. There is no party setup. You control one dude, all the time. Just one dude. Yes, NPC’s will follow you and they will “fight”, but they’re just background. They won’t ever contribute to a battle, or make a difference at all. In reality, it’s just you the main character, versus enemies.</p>

<p>There’s not even depth with items. There’s like 5 item types in the entire game!! What the hell is going on? How can this game be called “Final Fantasy”? The only items that make a difference are potions. So you’ll just use potions when you’re about to die. That’s all there is to the item system. What if you can’t afford potions? Don’t worry, every merchant in the game sells them very cheap, <em>and</em> they get automatically refilled if you die. Yeah, this game is a joke, mechanically. If you see this game as just its game systems and mechanics, it’s going to be extremely disappointing. There’s no way around it.</p>

<p>You know, I’m not necessarily against this! Not every videogame has to have very deep gameplay systems. A game can be about the visual art, or about the story, or the experience. It’s totally okay! I just think this game could have benefited from more system depth. The game is just too basic for how long it is. Also it’s called “Final Fantasy”, so you have some expectations going in that will get crushed.</p>

<h1 id="the-combat-is-bad-too">The Combat Is Bad Too.</h1>

<p>So, about the combat. Yeah, it’s bad. Stop fooling yourselves. The combat is also bad. This fact is obvious if you recently played a game with an actual good combat system, like Nioh (yeah I just beat that game. good game). I’m gonna break up my argument into bullet points:</p>

<ul>
  <li>The basic abilities are useless</li>
  <li>So few slots for the actual impactful abilities</li>
  <li>Most abilities are useless.</li>
  <li>No elemental systems. Yeah, all types of damage are the SAME. You don’t even have to think about what kind of damage you do to the enemies. You just spam abilities.</li>
  <li>The main point:</li>
</ul>

<p>There are very few “types” of things you can do in combat. The abilities are basically just “deal damage”, with a different visual flair. The only interesting abilities are the ones you need to time them to avoid damage and to deal more damage, aka a “parry”. The Titan set is one of the few ability sets that make the combat a bit more fun, because you need timing to “parry” attacks, and to block. The vast majority of abilities are simply “you deal damage”. Sometimes, the ability is an area-of-effect, so it’s better for multiple enemies.</p>

<p>There’s another type of abilities that are so, so bad: The ones that you need to wait and interrupt the flow of combat for them to trigger. A lot of “Circle” abilities are like this, for many ability sets. It just makes the game even more boring, because you just have to wait around away from enemies for a bit, so you can finally deal damage. They add nothing to the combat.</p>

<p>The other thing you will do in combat is dodge. Press R1 when you’re about to get hit.</p>

<p>That’s literally what the combat in this game is: Press R1 to dodge, while you spam all abilities available. You then wait for all the cooldowns, and you repeat. There is no thought. There is no depth. There’s no strategy. It’s just R1, and R2 + face button.</p>

<p>Sometimes, enemies have interesting patterns you have to learn to dodge attacks. I’ll give it that. Some bosses are fun to play against.</p>

<p>The main scenario sometimes breaks up the monotony in the action, with Eikon battles. These are cool. Visually stunning. It’s one of the strengths in the game. But there are long stretches in the game in which nothing interesting will happen, and you will be very bored. I don’t think the DLC is worth it. I regret buying the 2 DLC’s.</p>

<h1 id="to-balance-this-out-the-game-isnt-really-that-bad">To Balance This out: The Game Isn’t Really That Bad</h1>

<p>I still think the game, as a whole, is a good game. I generally had a good time with it. I think the story, the worldbuilding, the music, the visual spectacle, make this game all worth it. As a whole, it makes for a good, memorable experience. I’ve been very negative in this article, only because I’m focusing on the negative. After getting through the whole game, I couldn’t believe what people were saying online about its combat. I don’t think this combat is good. It has many problems. It can be fun for a few hours but not for many more.</p>

<p>I’d say they really need to add depth to their systems in the next game. Or just have less systems. Add more party members, maybe? The party system is one of the staples of the franchise. It’s a shame you only play as one dude in this entry.</p>

<p>Overly simple systems are not necessarily bad, but for a supposed “RPG Epic adventure” like Final Fantasy, which is dozens of hours long, you <em>really</em> need system depth to keep me engaged. Either that, or give me a serious mechanical challenge, in the style of Sekiro.</p>

<p>I think the FF7 Remake series has a much, much better combat system. They should get inspired by it, for Final Fantasy 17. Though I only played the first FF7 Remake. Will play Rebirth soon.</p>

<h1 id="screenshots--videos">Screenshots / Videos</h1>

<p>The game look very good tho’. I took some screenshots</p>

<p><img src="../assets/ff16/19dd3f124113-screenshotUrl.jpg" alt="ff16 screenshot" /></p>

<p><img src="../assets/ff16/19dd3f1241119-screenshotUrl.jpg" alt="ff16 screenshot" /></p>

<p><img src="../assets/ff16/19dd3f1241122-screenshotUrl.jpg" alt="ff16 screenshot" /></p>

<p><img src="../assets/ff16/19dd3f1241171-screenshotUrl.jpg" alt="ff16 screenshot" /></p>

<p>The combat flows very well sometimes.</p>

<video controls="">
  <source src="../assets/ff16/19dd3f488d090-master_playlist.mp4" type="video/mp4" />
</video>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="gaming" /><summary type="html"><![CDATA[I’ve seen people praise the combat of this game, so I had to speak up. I’ve even seen people who didn’t like the game overall say that the combat is “solid” and the strongest aspect of the game. I disagree. This game is a mixed bag. Even after starting to play it months ago, having beaten it, plus the DLC (I regret buying that), and having played it for a total of about 40 hours perhaps, I’m still not sure what to think of this game. It has high highs but also low lows. The visual spectacle on some sequences are the best gaming has to offer. The political intrigue, the worldbuilding, and the plot are excellent. The Active Lore System, or whatever they call it, is very innovative and serves a real need the game has. There is so much good about this game. But there’s also a lot of bad: Most characters are bland. The game has a lot of filler. Sometimes the game feels like a boring MMO. Some gameplay systems are extremely shallow, like the crafting system, and the gear system in general. It is such a strange game to critique. The game also has a huge problem with difficulty: Throughout the vast majority of the game, I didn’t feel challenged in any way. It seems like they streamlined the game for a broad audience a little bit too much. The game has many accessibility options, which I think it’s great, but it only goes one way: It provides accessibility to the casual gamer. But if you are someone who requires to be mechanically challenged in a game to stay engaged, there’s nothing for you. They only have one hard difficulty mode, but you have to first beat the game to unlock it. Which makes it useless. There’s no way in hell I’m playing this game again. Are you serious? They seriously have to stop locking difficulty modes like this. There isn’t a single gameplay system in the game with any kind of decent depth: The crafting system. The Gear system. The combat build system. The dog system. It’s all… empty. Like they were legally required to add these systems and that’s it. There’s nothing to them! Seriously, you literally will just pick the sword with the single stat that is higher (Swords have two stats, but they are always about the same value anyway), and that’s basically it! It’s an absolute joke. There is basically no RPG mechanics in this game. None. You can give commands to your dog, but it’s all filler and useless. The dog won’t ever make a difference in a battle. There is no party setup. You control one dude, all the time. Just one dude. Yes, NPC’s will follow you and they will “fight”, but they’re just background. They won’t ever contribute to a battle, or make a difference at all. In reality, it’s just you the main character, versus enemies. There’s not even depth with items. There’s like 5 item types in the entire game!! What the hell is going on? How can this game be called “Final Fantasy”? The only items that make a difference are potions. So you’ll just use potions when you’re about to die. That’s all there is to the item system. What if you can’t afford potions? Don’t worry, every merchant in the game sells them very cheap, and they get automatically refilled if you die. Yeah, this game is a joke, mechanically. If you see this game as just its game systems and mechanics, it’s going to be extremely disappointing. There’s no way around it. You know, I’m not necessarily against this! Not every videogame has to have very deep gameplay systems. A game can be about the visual art, or about the story, or the experience. It’s totally okay! I just think this game could have benefited from more system depth. The game is just too basic for how long it is. Also it’s called “Final Fantasy”, so you have some expectations going in that will get crushed. The Combat Is Bad Too. So, about the combat. Yeah, it’s bad. Stop fooling yourselves. The combat is also bad. This fact is obvious if you recently played a game with an actual good combat system, like Nioh (yeah I just beat that game. good game). I’m gonna break up my argument into bullet points: The basic abilities are useless So few slots for the actual impactful abilities Most abilities are useless. No elemental systems. Yeah, all types of damage are the SAME. You don’t even have to think about what kind of damage you do to the enemies. You just spam abilities. The main point: There are very few “types” of things you can do in combat. The abilities are basically just “deal damage”, with a different visual flair. The only interesting abilities are the ones you need to time them to avoid damage and to deal more damage, aka a “parry”. The Titan set is one of the few ability sets that make the combat a bit more fun, because you need timing to “parry” attacks, and to block. The vast majority of abilities are simply “you deal damage”. Sometimes, the ability is an area-of-effect, so it’s better for multiple enemies. There’s another type of abilities that are so, so bad: The ones that you need to wait and interrupt the flow of combat for them to trigger. A lot of “Circle” abilities are like this, for many ability sets. It just makes the game even more boring, because you just have to wait around away from enemies for a bit, so you can finally deal damage. They add nothing to the combat. The other thing you will do in combat is dodge. Press R1 when you’re about to get hit. That’s literally what the combat in this game is: Press R1 to dodge, while you spam all abilities available. You then wait for all the cooldowns, and you repeat. There is no thought. There is no depth. There’s no strategy. It’s just R1, and R2 + face button. Sometimes, enemies have interesting patterns you have to learn to dodge attacks. I’ll give it that. Some bosses are fun to play against. The main scenario sometimes breaks up the monotony in the action, with Eikon battles. These are cool. Visually stunning. It’s one of the strengths in the game. But there are long stretches in the game in which nothing interesting will happen, and you will be very bored. I don’t think the DLC is worth it. I regret buying the 2 DLC’s. To Balance This out: The Game Isn’t Really That Bad I still think the game, as a whole, is a good game. I generally had a good time with it. I think the story, the worldbuilding, the music, the visual spectacle, make this game all worth it. As a whole, it makes for a good, memorable experience. I’ve been very negative in this article, only because I’m focusing on the negative. After getting through the whole game, I couldn’t believe what people were saying online about its combat. I don’t think this combat is good. It has many problems. It can be fun for a few hours but not for many more. I’d say they really need to add depth to their systems in the next game. Or just have less systems. Add more party members, maybe? The party system is one of the staples of the franchise. It’s a shame you only play as one dude in this entry. Overly simple systems are not necessarily bad, but for a supposed “RPG Epic adventure” like Final Fantasy, which is dozens of hours long, you really need system depth to keep me engaged. Either that, or give me a serious mechanical challenge, in the style of Sekiro. I think the FF7 Remake series has a much, much better combat system. They should get inspired by it, for Final Fantasy 17. Though I only played the first FF7 Remake. Will play Rebirth soon. Screenshots / Videos The game look very good tho’. I took some screenshots The combat flows very well sometimes.]]></summary></entry><entry><title type="html">I made an Odin formatter</title><link href="/blog/lucyfmt.html" rel="alternate" type="text/html" title="I made an Odin formatter" /><published>2026-04-25T00:00:00+00:00</published><updated>2026-04-25T00:00:00+00:00</updated><id>/blog/lucyfmt</id><content type="html" xml:base="/blog/lucyfmt.html"><![CDATA[<p><a href="https://github.com/lucypero/lucyfmt">Link to source</a></p>

<p>I made a very minimalist Odin formatter for myself. I have tried <a href="https://github.com/DanielGavin/ols/tree/master/tools/odinfmt">odinfmt</a> in the past. I have nothing against it, but I prefer something that leaves the code more as it is. I like it when the formatter lets me add newlines to anything, and doesn’t ever introduce newlines. That’s a big one for me.</p>

<p>So, lucyfmt’s killer features are:</p>

<ul>
  <li>It leaves your code alone! Plus, it does the following:</li>
  <li>Indents lines with 1 tab per scope level, except for <code class="language-plaintext highlighter-rouge">when</code> blocks.</li>
  <li>Adds one indent level to parameters if they were broken up into multiple lines.</li>
  <li>There’s no way to configure this formatter. This is a great feature, actually. I’ll tell you why:
    <ul>
      <li>Keeps it simple.</li>
      <li>There’s no way for you to screw it up.</li>
      <li>Every file formatted with lucyfmt is formatted exactly the same.</li>
    </ul>
  </li>
</ul>

<h1 id="showcase">Showcase</h1>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Adds indentation, **only** if you break up a function call in multiple lines.</span>
<span class="n">ct</span><span class="p">.</span><span class="n">window</span> <span class="o">=</span> <span class="n">sdl</span><span class="p">.</span><span class="nf">CreateWindow</span><span class="p">(</span>
	<span class="s">"lucydx12"</span><span class="p">,</span>
	<span class="n">sdl</span><span class="p">.</span><span class="n">WINDOWPOS_UNDEFINED</span><span class="p">,</span>
	<span class="n">sdl</span><span class="p">.</span><span class="n">WINDOWPOS_UNDEFINED</span><span class="p">,</span>
	<span class="n">WINDOW_WIDTH</span><span class="p">,</span>
	<span class="n">WINDOW_HEIGHT</span><span class="p">,</span>
	<span class="p">{.</span><span class="n">ALLOW_HIGHDPI</span><span class="p">,</span> <span class="p">.</span><span class="n">SHOWN</span><span class="p">,</span> <span class="p">.</span><span class="n">RESIZABLE</span><span class="p">},</span>
<span class="p">)</span>
</code></pre></div></div>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// This will _not_ get broken up into multiple lines! lucyfmt respects the programmer.</span>
<span class="n">ct</span><span class="p">.</span><span class="n">window</span> <span class="o">=</span> <span class="n">sdl</span><span class="p">.</span><span class="nf">CreateWindow</span><span class="p">(</span><span class="s">"lucydx12"</span><span class="p">,</span> <span class="n">sdl</span><span class="p">.</span><span class="n">WINDOWPOS_UNDEFINED</span><span class="p">,</span> <span class="n">sdl</span><span class="p">.</span><span class="n">WINDOWPOS_UNDEFINED</span><span class="p">,</span> <span class="n">WINDOW_WIDTH</span><span class="p">,</span> <span class="n">WINDOW_HEIGHT</span><span class="p">,</span> <span class="p">{.</span><span class="n">ALLOW_HIGHDPI</span><span class="p">,</span> <span class="p">.</span><span class="n">SHOWN</span><span class="p">,</span> <span class="p">.</span><span class="n">RESIZABLE</span><span class="p">})</span>
</code></pre></div></div>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Same to struct initializers</span>
<span class="n">to_render_target_barrier</span> <span class="o">:=</span> <span class="n">dx</span><span class="p">.</span><span class="n">RESOURCE_BARRIER</span> <span class="p">{</span>
	<span class="n">Type</span> <span class="o">=</span> <span class="p">.</span><span class="n">TRANSITION</span><span class="p">,</span>
	<span class="n">Flags</span> <span class="o">=</span> <span class="p">{},</span>
	<span class="n">Transition</span> <span class="o">=</span> <span class="p">{</span>
		<span class="n">pResource</span> <span class="o">=</span> <span class="n">g_dx_context</span><span class="p">.</span><span class="n">targets</span><span class="p">[</span><span class="n">g_dx_context</span><span class="p">.</span><span class="n">frame_index</span><span class="p">],</span>
		<span class="n">StateBefore</span> <span class="o">=</span> <span class="n">dx</span><span class="p">.</span><span class="n">RESOURCE_STATE_PRESENT</span><span class="p">,</span>
		<span class="n">StateAfter</span> <span class="o">=</span> <span class="p">{.</span><span class="n">RENDER_TARGET</span><span class="p">},</span>
		<span class="n">Subresource</span> <span class="o">=</span> <span class="n">dx</span><span class="p">.</span><span class="n">RESOURCE_BARRIER_ALL_SUBRESOURCES</span><span class="p">,</span>
	<span class="p">},</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// `case` lines don't get indented.</span>
<span class="k">for</span> <span class="o">&amp;</span><span class="n">s</span> <span class="k">in</span> <span class="n">g_scenes</span> <span class="p">{</span>
	<span class="n">st</span> <span class="o">:=</span> <span class="nf">scene_status_load</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">.</span><span class="n">status</span><span class="p">)</span>
	<span class="cp">#partial</span> <span class="k">switch</span> <span class="n">st</span> <span class="p">{</span>
	<span class="k">case</span> <span class="p">.</span><span class="n">Ready</span><span class="p">:</span>
		<span class="nf">scene_status_store</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">.</span><span class="n">status</span><span class="p">,</span> <span class="p">.</span><span class="n">QueuedForDeletion</span><span class="p">)</span>
	<span class="k">case</span> <span class="p">.</span><span class="n">Free</span><span class="p">:</span>
		<span class="k">if</span> <span class="o">!</span><span class="n">found_free</span> <span class="p">{</span>
			<span class="nf">scene_schedule_load</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="n">new_scene</span><span class="p">)</span>
		<span class="p">}</span>
		<span class="n">found_free</span> <span class="o">=</span> <span class="kc">true</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-odin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// It doesn't over-indent if you open more than 1 bracket or paren in the same line.</span>
<span class="nf">scene_walk</span><span class="p">(</span><span class="n">scene</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="k">proc</span><span class="p">(</span><span class="n">node</span><span class="p">:</span> <span class="n">Node</span><span class="p">,</span> <span class="n">scene</span><span class="p">:</span> <span class="n">Scene</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="kt">rawptr</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">ct</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">g_dx_context</span>

	<span class="k">if</span> <span class="n">node</span><span class="p">.</span><span class="n">mesh</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="p">{</span>
		<span class="k">return</span>
	<span class="p">}</span>

	<span class="n">mesh_to_render</span> <span class="o">:=</span> <span class="n">scene</span><span class="p">.</span><span class="n">meshes</span><span class="p">[</span><span class="n">node</span><span class="p">.</span><span class="n">mesh</span><span class="p">]</span>

	<span class="k">for</span> <span class="n">prim</span> <span class="k">in</span> <span class="n">mesh_to_render</span><span class="p">.</span><span class="n">primitives</span> <span class="p">{</span>
		<span class="n">dc</span> <span class="o">:=</span> <span class="n">DrawConstants</span> <span class="p">{</span>
			<span class="n">mesh_index</span> <span class="o">=</span> <span class="kt">u32</span><span class="p">(</span><span class="n">g_mesh_drawn_count</span><span class="p">),</span>
			<span class="n">material_index</span> <span class="o">=</span> <span class="kt">u32</span><span class="p">(</span><span class="n">prim</span><span class="p">.</span><span class="n">material_index</span><span class="p">),</span>
		<span class="p">}</span>
		<span class="n">ct</span><span class="p">.</span><span class="n">cmdlist</span><span class="o">-&gt;</span><span class="nf">SetGraphicsRoot32BitConstants</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dc</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
		<span class="n">ct</span><span class="p">.</span><span class="n">cmdlist</span><span class="o">-&gt;</span><span class="nf">DrawIndexedInstanced</span><span class="p">(</span><span class="n">prim</span><span class="p">.</span><span class="n">index_count</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">prim</span><span class="p">.</span><span class="n">index_offset</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<p><a href="https://github.com/lucypero/lucydx12">LucyDX12</a> is formatted with lucyfmt. Why did you think the code looks so pretty?</p>

<h1 id="feedback">Feedback</h1>

<p>I want other people to use this. I’m sure it’s not only me that I want something like this. So please, let me know if you are interested in using this, but it’s not exactly to your liking. Maybe we can make it work.</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="programming" /><category term="odin" /><summary type="html"><![CDATA[Link to source I made a very minimalist Odin formatter for myself. I have tried odinfmt in the past. I have nothing against it, but I prefer something that leaves the code more as it is. I like it when the formatter lets me add newlines to anything, and doesn’t ever introduce newlines. That’s a big one for me. So, lucyfmt’s killer features are: It leaves your code alone! Plus, it does the following: Indents lines with 1 tab per scope level, except for when blocks. Adds one indent level to parameters if they were broken up into multiple lines. There’s no way to configure this formatter. This is a great feature, actually. I’ll tell you why: Keeps it simple. There’s no way for you to screw it up. Every file formatted with lucyfmt is formatted exactly the same. Showcase // Adds indentation, **only** if you break up a function call in multiple lines. ct.window = sdl.CreateWindow( "lucydx12", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, {.ALLOW_HIGHDPI, .SHOWN, .RESIZABLE}, ) // This will _not_ get broken up into multiple lines! lucyfmt respects the programmer. ct.window = sdl.CreateWindow("lucydx12", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, {.ALLOW_HIGHDPI, .SHOWN, .RESIZABLE}) // Same to struct initializers to_render_target_barrier := dx.RESOURCE_BARRIER { Type = .TRANSITION, Flags = {}, Transition = { pResource = g_dx_context.targets[g_dx_context.frame_index], StateBefore = dx.RESOURCE_STATE_PRESENT, StateAfter = {.RENDER_TARGET}, Subresource = dx.RESOURCE_BARRIER_ALL_SUBRESOURCES, }, } // `case` lines don't get indented. for &amp;s in g_scenes { st := scene_status_load(&amp;s.status) #partial switch st { case .Ready: scene_status_store(&amp;s.status, .QueuedForDeletion) case .Free: if !found_free { scene_schedule_load(&amp;s, new_scene) } found_free = true } } // It doesn't over-indent if you open more than 1 bracket or paren in the same line. scene_walk(scene, nil, proc(node: Node, scene: Scene, data: rawptr) { ct := &amp;g_dx_context if node.mesh == -1 { return } mesh_to_render := scene.meshes[node.mesh] for prim in mesh_to_render.primitives { dc := DrawConstants { mesh_index = u32(g_mesh_drawn_count), material_index = u32(prim.material_index), } ct.cmdlist-&gt;SetGraphicsRoot32BitConstants(0, 2, &amp;dc, 0) ct.cmdlist-&gt;DrawIndexedInstanced(prim.index_count, 1, prim.index_offset, 0, 0) } }) LucyDX12 is formatted with lucyfmt. Why did you think the code looks so pretty? Feedback I want other people to use this. I’m sure it’s not only me that I want something like this. So please, let me know if you are interested in using this, but it’s not exactly to your liking. Maybe we can make it work.]]></summary></entry><entry><title type="html">The Anti-AI bro Problem</title><link href="/blog/anti-ai.html" rel="alternate" type="text/html" title="The Anti-AI bro Problem" /><published>2026-03-30T00:00:00+00:00</published><updated>2026-03-30T00:00:00+00:00</updated><id>/blog/anti-ai-bro</id><content type="html" xml:base="/blog/anti-ai.html"><![CDATA[<p>I brushed on the topic of “AI bros” on my <a href="./agentic-coding">last article</a>. Basically, there’s a huge group of people online that do the following:</p>

<ul>
  <li>Make huge misrepresentations of the power of current AI tools, and what this tech is <em>inherently</em>.</li>
  <li>Make wild assumptions about the future of the technology.</li>
  <li>The implied utter disrespect towards Art, engineering, and human expertise.</li>
  <li>Cult-like behavior: If you slightly disagree with them about AI, you are cast out.</li>
</ul>

<p>I think the problems with AI bros are pretty clear. So today, I’ll talk about a different group: The “Anti-AI” bros.</p>

<h1 id="the-anti-ai-bro">The Anti-AI Bro:</h1>

<ul>
  <li>Completely dismisses one’s arguments or work the moment that they suspect that AI was involved</li>
  <li>Their IQ drops by half the instant that AI becomes the topic of conversation.</li>
  <li>Is proudly ignorant of the advances of the tech.</li>
  <li>Never tries the tech.</li>
  <li>Doesn’t know the tech.</li>
  <li>Doesn’t know the strengths and limitations of the tech, but…</li>
  <li>They are confident that it’s all bad.</li>
</ul>

<h1 id="being-like-this-is-being-a-bad-engineer">Being like this is being a bad engineer.</h1>

<p>Good engineers try new tools, constantly, to see if they’re useful for whatever they are doing. To make their product better. To save time and costs. Why would AI tools be an exception to this?</p>

<p>Example of the worst of Anti-AI bro behavior: <a href="https://www.rockpapershotgun.com/larian-boss-responds-to-criticism-of-generative-ai-use-its-something-we-are-constantly-discussing-internally">Larian dares to try AI</a></p>

<p>Larian just made what is widely considered to be one of the greatest games ever made: Baldur’s Gate 3. But that wasn’t enough to the anti-AI bro. Larian dared to try AI to improve some workflows, so they have “sinned”. Any abuse thrown towards them is now virtuous and justified.</p>

<p>Please, don’t be like that.</p>

<h1 id="personal-experiences">Personal experiences</h1>

<p>I had to take a break from social media because of how badly anti-ai bros treated me. They get very irrational and heated the moment that this topic is brought up. Even in programming circles. And it doesn’t matter <em>how</em> you bring it up, or how well you express your arguments, or how much evidence you bring to the table. None of that matters. Like I said, their IQ drops by half the moment AI is brought up. They get into an irrational state of mind so there’s nothing you can say to them to have a productive conversation.</p>

<p>It’s very sad, as the consequences of this are: People will simply stop talking about these tools, in fear of backlash. They will silently keep using this technology, explore what it’s good at, what it’s bad at. But now they have to do it in secret. And that’s not great: Things are better when we can openly talk about things.</p>

<h1 id="dismissal-of-ai-tools-as-a-tool-for-learning">Dismissal of AI Tools as a tool for learning.</h1>

<p>One use case for AI I love is learning: I use Gemini to learn about all kinds of topics. Thanks to this tool, I was able to learn, in a very fun and efficient way, about: Genetics, Music theory, how LLM’s work. Basically, any question that comes up, I can quickly go to the Gemini website and learn anything I want about it. This tool is literally making me more educated. Should you fact check it, regularly? Can it hallucinate facts? Yes, of course. But it’s still an invaluable resource for summarizing information. I’d go as far as to say that LLM’s are excellent at this.</p>

<p>I’ve been working on a DirectX12 project. I’m making a DX12 renderer. It’s going very well. And a huge reason for that is Gemini. Gemini was extremely helpful at teaching me DX12 concepts, and general rendering techniques. It walks me through different techniques and rendering methods, and how to apply DX12 features. It lets me know of newer DX12 or HLSL features that I would otherwise not know about.</p>

<p>And all of this, I verified. Gemini isn’t making it all up. I know this. How? Because my program works. Because I double check information with the Microsoft Docs, with experts, and other sources. I scientifically verified what Gemini tells me, and I found that it’s accurate. Is it 100% accurate? No! But no tool is perfect!</p>

<p>But the moment you tell an AI bro this, they will confidently say that whatever you think you learned is garbage and lies, and that you should go and pick up a book instead, or read API documentation directly. Ok, boomer!</p>

<h1 id="it-helps-with-art-too">It helps with Art too.</h1>

<p>Yeah. I’m going there.</p>

<p>We have professional artists at our game studio. They do wonderful work. They recently found ways to use AI to accelerate content creation, while still maintaining their ideas and intentions of their concept art. Yes, it’s a compromise, but we found that in some cases, it’s very useful to us. The technology might improve even further and let artists produce more game content while still having artistic control over the output. AI is still very dodgy in this area, but it’s beginning to show promise.</p>

<h1 id="please-stop">Please, stop.</h1>

<p>Please, be rational about things. Be measured. Try things out. Don’t be a closed-minded neanderthal. That’s all I’m asking.</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="ai" /><category term="programming" /><summary type="html"><![CDATA[I brushed on the topic of “AI bros” on my last article. Basically, there’s a huge group of people online that do the following: Make huge misrepresentations of the power of current AI tools, and what this tech is inherently. Make wild assumptions about the future of the technology. The implied utter disrespect towards Art, engineering, and human expertise. Cult-like behavior: If you slightly disagree with them about AI, you are cast out. I think the problems with AI bros are pretty clear. So today, I’ll talk about a different group: The “Anti-AI” bros. The Anti-AI Bro: Completely dismisses one’s arguments or work the moment that they suspect that AI was involved Their IQ drops by half the instant that AI becomes the topic of conversation. Is proudly ignorant of the advances of the tech. Never tries the tech. Doesn’t know the tech. Doesn’t know the strengths and limitations of the tech, but… They are confident that it’s all bad. Being like this is being a bad engineer. Good engineers try new tools, constantly, to see if they’re useful for whatever they are doing. To make their product better. To save time and costs. Why would AI tools be an exception to this? Example of the worst of Anti-AI bro behavior: Larian dares to try AI Larian just made what is widely considered to be one of the greatest games ever made: Baldur’s Gate 3. But that wasn’t enough to the anti-AI bro. Larian dared to try AI to improve some workflows, so they have “sinned”. Any abuse thrown towards them is now virtuous and justified. Please, don’t be like that. Personal experiences I had to take a break from social media because of how badly anti-ai bros treated me. They get very irrational and heated the moment that this topic is brought up. Even in programming circles. And it doesn’t matter how you bring it up, or how well you express your arguments, or how much evidence you bring to the table. None of that matters. Like I said, their IQ drops by half the moment AI is brought up. They get into an irrational state of mind so there’s nothing you can say to them to have a productive conversation. It’s very sad, as the consequences of this are: People will simply stop talking about these tools, in fear of backlash. They will silently keep using this technology, explore what it’s good at, what it’s bad at. But now they have to do it in secret. And that’s not great: Things are better when we can openly talk about things. Dismissal of AI Tools as a tool for learning. One use case for AI I love is learning: I use Gemini to learn about all kinds of topics. Thanks to this tool, I was able to learn, in a very fun and efficient way, about: Genetics, Music theory, how LLM’s work. Basically, any question that comes up, I can quickly go to the Gemini website and learn anything I want about it. This tool is literally making me more educated. Should you fact check it, regularly? Can it hallucinate facts? Yes, of course. But it’s still an invaluable resource for summarizing information. I’d go as far as to say that LLM’s are excellent at this. I’ve been working on a DirectX12 project. I’m making a DX12 renderer. It’s going very well. And a huge reason for that is Gemini. Gemini was extremely helpful at teaching me DX12 concepts, and general rendering techniques. It walks me through different techniques and rendering methods, and how to apply DX12 features. It lets me know of newer DX12 or HLSL features that I would otherwise not know about. And all of this, I verified. Gemini isn’t making it all up. I know this. How? Because my program works. Because I double check information with the Microsoft Docs, with experts, and other sources. I scientifically verified what Gemini tells me, and I found that it’s accurate. Is it 100% accurate? No! But no tool is perfect! But the moment you tell an AI bro this, they will confidently say that whatever you think you learned is garbage and lies, and that you should go and pick up a book instead, or read API documentation directly. Ok, boomer! It helps with Art too. Yeah. I’m going there. We have professional artists at our game studio. They do wonderful work. They recently found ways to use AI to accelerate content creation, while still maintaining their ideas and intentions of their concept art. Yes, it’s a compromise, but we found that in some cases, it’s very useful to us. The technology might improve even further and let artists produce more game content while still having artistic control over the output. AI is still very dodgy in this area, but it’s beginning to show promise. Please, stop. Please, be rational about things. Be measured. Try things out. Don’t be a closed-minded neanderthal. That’s all I’m asking.]]></summary></entry><entry><title type="html">Agentic Coding - It’s not that simple.</title><link href="/blog/agentic-coding.html" rel="alternate" type="text/html" title="Agentic Coding - It’s not that simple." /><published>2026-03-26T00:00:00+00:00</published><updated>2026-03-26T00:00:00+00:00</updated><id>/blog/agentic-coding-my-current-thoughts</id><content type="html" xml:base="/blog/agentic-coding.html"><![CDATA[<p>I’ve been using Claude Code extensively at work and for little experiments on my own time. I’m here to reflect on my experiences.</p>

<p>My opinions on Agentic Coding are wildly fluctuating still, but these are my thoughts right now, at least.</p>

<p>We’ve been using Claude Code for about a month to make Unity games. The idea was to let the model do all the coding. We just manage it. Some coworkers like to run many instances at once. I haven’t found that to be helpful, personally. What I often do is have a file with a checklist of tasks. I have Claude read it and interview me about all the tasks, and then I let it work. It works OK in most cases.</p>

<h1 id="early-experiences">Early experiences</h1>

<p>When I started using this, I was amazed. I was so pro agentic coding. It seemed magical. You just tell it what to do and it does it. It accelerated work by so much. The problems only come later when the project grows and you need to start understanding the code. You should not let this tool do the thinking for you. You need to at least know how it’s architecting things. Otherwise, it’s gonna bite you later. I also found it to not be consistent: It sometimes manages to solve relatively complex problems on the first try, no problem. But then it gets stuck with really simple tasks. It’s really strange.</p>

<h1 id="the-alignment-problem">The Alignment Problem</h1>

<p>I discovered that Claude Code and probably all other commercial LLM’s have a problem with following directions and guidelines. They will follow whatever training they got and not take your guidelines into account. It was really hard for me to mold its coding style to my liking, even with simple things.</p>

<p>We code in Unity C# at work. I tried super hard to make it STOP null checking everything. Doing this is very bad as it makes the code much harder to read, longer, and it turns bugs “silent”. As in: If something that should not be null is actually null, the code would fail silently with Claude’s null checks. No exceptions thrown. Just nothing. This is really bad for debugging.</p>

<p>I tried so hard to make it NOT null check. I wrote clear guidelines in the Claude.md file. I had an intervention session with Claude explaining this issue. I introduced checks in pre-commit hooks and in claude’s tool usage. It still constantly tries to do the null checks. It tries to bypass checks. It rationalizes useless null checks.</p>

<p>I find this to be a huge fundamental problem with this technology, as the model that you get is “frozen”. The training is already done. You can’t train it yourself. If we can’t mold AI behavior to our liking, it’s going to be very limited. I’m not a LLM engineer so I don’t know what the solution for this should be.</p>

<h1 id="the-dangers">The Dangers</h1>

<p>The more you use this, the more you depend on it. The less you know the codebase. The more useless you become as the engineer of the project. I find this to be very dangerous. It’s inevitable that there’s going to be a point where the project will get too big and convoluted for you to make changes and additions productively. The AI will not be able to cope with the project’s complexity, eventually. And there’s a risk that your project will fail. I think this should be talked about more.</p>

<p>I found that it often writes code in a very unnecessarily convoluted way. Code that is spread out across functions and files that should all be in one place. Code that could be much shorter. Also, it introduces dangerous bugs. I made it code a small Odin project. I was very impressed by it at first, but then I looked at the code and it’s not pretty. It also introduced a very basic use-after-free bug. Something that an experienced Odin programmer would never do.</p>

<p>This tool is still very useful but it should be used with caution: Make sure you understand the high level view of the systems that you are making, at least. Read the code it writes. Order the AI to refactor it periodically so it’s simpler.</p>

<p>The fact that, in general, advocates of these tools never talk about the limitations of this technology is very concerning to me. No, they cannot replace an engineer (at least not me). It’s just a tool with strengths and weaknesses. It should always be seen as this, instead of some magical thing that replaces human minds.</p>

<h1 id="its-excellent-for-grunt-work-and-tooling">It’s excellent for “grunt work” and tooling</h1>

<p>I found this tool to be most suitable for “boring” tasks. Create new scripts and prefabs. Handle commands. Write small scripts that do a very specific thing. It’s great for that and it saves you so much time, for the things that you don’t really want to do, are not interesting, and don’t impact the project’s architecture in a high level way. It’s also great for setting up the boilerplate for new systems, as long as you are the one deciding how this system is implemented.</p>

<p>One of its strengths is tooling creation. We make it do custom tooling for our games in Unity, and it’s fascinating. It often gets it right the first try. And we get really useful tooling to develop content in our games. Plus, it’s code that doesn’t have to ship to clients, so you can “let it rip” with minimal worrying. It’s a perfect application for this technology.</p>

<h1 id="current-aversion">Current aversion</h1>

<p>As of now, I feel an aversion to agentic coding. It’s much more fun for me to do the coding myself. I know how to make the code simpler and less error prone. It’s just better in many cases. But agentic coding can still accelerate a lot of work in a project, for sure.</p>

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

<p>It can be great. But it’s just a tool. Use it for what it’s good for and not for anything else. Don’t let it <em>think</em> for you as that will lead to disaster.</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="ai" /><category term="programming" /><summary type="html"><![CDATA[I’ve been using Claude Code extensively at work and for little experiments on my own time. I’m here to reflect on my experiences. My opinions on Agentic Coding are wildly fluctuating still, but these are my thoughts right now, at least. We’ve been using Claude Code for about a month to make Unity games. The idea was to let the model do all the coding. We just manage it. Some coworkers like to run many instances at once. I haven’t found that to be helpful, personally. What I often do is have a file with a checklist of tasks. I have Claude read it and interview me about all the tasks, and then I let it work. It works OK in most cases. Early experiences When I started using this, I was amazed. I was so pro agentic coding. It seemed magical. You just tell it what to do and it does it. It accelerated work by so much. The problems only come later when the project grows and you need to start understanding the code. You should not let this tool do the thinking for you. You need to at least know how it’s architecting things. Otherwise, it’s gonna bite you later. I also found it to not be consistent: It sometimes manages to solve relatively complex problems on the first try, no problem. But then it gets stuck with really simple tasks. It’s really strange. The Alignment Problem I discovered that Claude Code and probably all other commercial LLM’s have a problem with following directions and guidelines. They will follow whatever training they got and not take your guidelines into account. It was really hard for me to mold its coding style to my liking, even with simple things. We code in Unity C# at work. I tried super hard to make it STOP null checking everything. Doing this is very bad as it makes the code much harder to read, longer, and it turns bugs “silent”. As in: If something that should not be null is actually null, the code would fail silently with Claude’s null checks. No exceptions thrown. Just nothing. This is really bad for debugging. I tried so hard to make it NOT null check. I wrote clear guidelines in the Claude.md file. I had an intervention session with Claude explaining this issue. I introduced checks in pre-commit hooks and in claude’s tool usage. It still constantly tries to do the null checks. It tries to bypass checks. It rationalizes useless null checks. I find this to be a huge fundamental problem with this technology, as the model that you get is “frozen”. The training is already done. You can’t train it yourself. If we can’t mold AI behavior to our liking, it’s going to be very limited. I’m not a LLM engineer so I don’t know what the solution for this should be. The Dangers The more you use this, the more you depend on it. The less you know the codebase. The more useless you become as the engineer of the project. I find this to be very dangerous. It’s inevitable that there’s going to be a point where the project will get too big and convoluted for you to make changes and additions productively. The AI will not be able to cope with the project’s complexity, eventually. And there’s a risk that your project will fail. I think this should be talked about more. I found that it often writes code in a very unnecessarily convoluted way. Code that is spread out across functions and files that should all be in one place. Code that could be much shorter. Also, it introduces dangerous bugs. I made it code a small Odin project. I was very impressed by it at first, but then I looked at the code and it’s not pretty. It also introduced a very basic use-after-free bug. Something that an experienced Odin programmer would never do. This tool is still very useful but it should be used with caution: Make sure you understand the high level view of the systems that you are making, at least. Read the code it writes. Order the AI to refactor it periodically so it’s simpler. The fact that, in general, advocates of these tools never talk about the limitations of this technology is very concerning to me. No, they cannot replace an engineer (at least not me). It’s just a tool with strengths and weaknesses. It should always be seen as this, instead of some magical thing that replaces human minds. It’s excellent for “grunt work” and tooling I found this tool to be most suitable for “boring” tasks. Create new scripts and prefabs. Handle commands. Write small scripts that do a very specific thing. It’s great for that and it saves you so much time, for the things that you don’t really want to do, are not interesting, and don’t impact the project’s architecture in a high level way. It’s also great for setting up the boilerplate for new systems, as long as you are the one deciding how this system is implemented. One of its strengths is tooling creation. We make it do custom tooling for our games in Unity, and it’s fascinating. It often gets it right the first try. And we get really useful tooling to develop content in our games. Plus, it’s code that doesn’t have to ship to clients, so you can “let it rip” with minimal worrying. It’s a perfect application for this technology. Current aversion As of now, I feel an aversion to agentic coding. It’s much more fun for me to do the coding myself. I know how to make the code simpler and less error prone. It’s just better in many cases. But agentic coding can still accelerate a lot of work in a project, for sure. Conclusion It can be great. But it’s just a tool. Use it for what it’s good for and not for anything else. Don’t let it think for you as that will lead to disaster.]]></summary></entry><entry><title type="html">Hostile Metroidvania design — How Nine Sols wants you to get lost</title><link href="/blog/hostile-metroidvania.html" rel="alternate" type="text/html" title="Hostile Metroidvania design — How Nine Sols wants you to get lost" /><published>2025-04-03T00:00:00+00:00</published><updated>2025-04-03T00:00:00+00:00</updated><id>/blog/hostile-metroidvania</id><content type="html" xml:base="/blog/hostile-metroidvania.html"><![CDATA[<p><img src="../assets/images/ninesols.avif" alt="nine ols" />
<em>Cute little guy</em></p>

<p>I’ve been playing <a href="https://store.steampowered.com/app/1809540/Nine_Sols/"><em>Nine Sols</em></a> for about 13 hours and I think I won’t go back to it now.</p>

<p>For an overview of the game: It’s a decent metroidvania game with great hand-drawn visuals and a unique setting. They call it “Taopunk”, it’s quite interesting and original.</p>

<p>Apart from the visuals and setting, the game doesn’t stray too far from other games in this “metroidvania mixed with dark souls” genre. It’s serviceable. It plays it extremely safe. One of the main aspects of the combat is the <em>parry</em> mechanic. It’s a very overused mechanic recently in games, and from someone who has played <em>Sekiro</em> and loves it to death, and just beat <em>Lies of P</em>, the parry in this game is quite underwhelming. It just doesn’t feel very satisfying. Everything you do in combat doesn’t have a lot of <em>impact</em>, in fact.</p>

<p>But it’s fine! It’s a perfectly serviceable combat system. I’m not writing this article because of these things. We’re gonna talk about why I dropped the game; and what has frustrated me so much time and time again while trying to make progress in this damn game.</p>

<h1 id="hostile-navigation">Hostile navigation</h1>

<p>What ruined my experience playing <em>Nine Sols</em> is that the game tries its best to get you lost. This has been the hardest game to navigate that I have played in recent memory. I ran around in circles for hours. It’s infuriating. The game doesn’t even give you a hint about where to go next. The map viewer isn’t very helpful. Most areas are indistinguishable from each other. The map is very open with little sense of structured progression, so whenever you complete an area, chances are you’ll be back to square one and have absolutely no idea where to go next.</p>

<p>I’ll try to be as precise as I can explaining the problem: Progression <em>within</em> levels is good. The problem is when you finish a story beat and you’re done with your current area. Figuring out where you need to go next might take hours. What also happened in my playthrough is that sometimes when I seemed to have found the way, I explore, then after a while I realize that it’s an optional area and that I haven’t done any real progress, and I’m back at square one.</p>

<p>I’ve been having these thoughts about the game’s navigation for a while. I talked about it with other people and most seemed to agree in some way. Then I looked online and I saw a lot of people begging for help on various forums. This is a real problem in the game. But what really prompted me to actually write about this and start my vendetta against this game is the thought that… <em>they did this on purpose</em>.</p>

<h1 id="no-of-course-they-didnt-do-it-on-purpose-did-they-though">No, of course they didn’t do it <em>on purpose</em>. Did they, though?</h1>

<p>Yes, the problem is so bad that you can’t help but wonder. It gives me the slight impression that they intentionally want the player to get lost constantly, to pad out the game’s runtime.</p>

<p>Of course, I have no evidence to back up this theory, and I’m not sure if I actually believe it; but it doesn’t really matter. Either the game is intentionally obtuse about you finding your way to make the game longer without having to produce more content, or they think there’s nothing wrong with the navigational design in the game. Both options are equally bad.</p>

<p>Whether intentional or not, the game’s exploration design leads to a frustrating experience where the player feels deliberately starved of much needed information.</p>

<h1 id="failing-where-hollow-knight-succeeded">Failing where Hollow Knight succeeded</h1>

<p>For contrast, let’s talk about all the tools and information Hollow Knight gives you to navigate its giant open-ended world. It’s a very apt comparison as these games share the same “metroidvania/Dark Souls” genre.</p>

<ul>
  <li>Each biome is quite distinct in style and in enemy variety.</li>
  <li>The way you acquire more maps and information within those maps has a clear progression, resulting in a clear understanding of what you already explored versus uncharted territory</li>
  <li>Gamified navigational tools, like you can buy pins that automatically mark different things on the map. You can also buy custom pins that you can place wherever you want. This lets you keep a personalized record of your travels and what you’ve obsersed and haven’t observed.</li>
  <li>It is very clear when you can’t proceed because you lack a certain ability. This could be a transition between biomes (easy to remember), or something very memorable and distinct, like a black flowing barrier.</li>
  <li>The map view is much easier to understand.</li>
</ul>

<p><img src="../assets/images/hollow-knight-map.webp" alt="hollow-knight map" />
<em>Well defined areas with their unique shapes and landmarks</em></p>

<p><img src="../assets/images/hollow-knight-map-ingame.jpg" alt="hollow-knight map in game" />
<em>Pre-placed markers and custom markers for the player help a lot</em></p>

<p>The game has many more tools to orient the player that are more suble than this, but this is just what came to mind. Nine Sols lacks all of this.</p>

<h1 id="what-else-could-they-have-done">What else could they have done?</h1>

<p>I think I covered enough ideas in the Hollow Knight section, but here’s a couple more:</p>

<ul>
  <li>NPC’s informing you of your current quest, and talking to you about locations. Anything that could give you a clue about where you are and where you’re going. Maybe new NPC’s could call you with clues, or taunting you towards the next area.</li>
  <li>Some kind of progression log. This could be not subtle at all, like a literal Quest Status section on the menu; or <em>something</em> in the Hub that informs you of your current progress, like maybe a canvas on a wall that is progressively filled.</li>
  <li>A <em>better</em> map view. Differentiate explored areas with unexplored areas. Tempt me with a blacked out area in the map that I haven’t gone to yet. Something. Just do <em>something</em> to help.</li>
</ul>

<h1 id="rebutting-some-counter-points">Rebutting some counter points</h1>

<h3 id="the-ai-guy-tells-you-where-to-go">“The AI guy tells you where to go”</h3>

<p>There’s a robot/AI furry person/thing inside your hub. Sometimes (very rarely), it will tell you something that could be considered “useful”. But:</p>

<ul>
  <li>The AI guy will very rarely tell you where to go.</li>
  <li>If it does tell you, it won’t be very specific.</li>
  <li><strong>If it does tell you, it will never tell you again</strong>. It will only tell you <em>once</em>. Subsequent dialogue prompts will be about something useless. That means that if you accidentally skipped that line, or you forgot what it said, there is <em>no way</em> to get that information inside of the game.</li>
  <li>Why would they make important information like that so easily missable?</li>
</ul>

<h3 id="sometimes-the-game-marks-the-map-for-where-to-go-next">“Sometimes, the game marks the map for where to go next”</h3>

<ul>
  <li>Yes, but it does this so, so, <em>so</em> rarely.</li>
  <li><strong>Sometimes, the game will point towards a dead end</strong>. This has actually happened to me. It pointed me somewhere, but at the end of that area, I needed a key to proceed. Where do you get the key? Good luck with that!</li>
  <li>Just like the previous point, <strong>they only mark it on the map once</strong>. After you saw it for the first time, the marker disappears forever.</li>
  <li>Why? Why would they do this?</li>
</ul>

<p><img src="../assets/images/ninesols-map.webp" alt="the horrible map" />
<em>The overview map isn’t very useful… It’s pretty, though!</em></p>

<h3 id="its-about-exploring-past-areas-until-you-find-the-next-thing-you-have-to-take-it-slow">“It’s about exploring past areas until you find the next thing! You have to take it slow!”</h3>

<p>This isn’t good enough for me anymore. I have much less tolerance for a game wasting my time than when I was younger. This just doesn’t cut it anymore. If you want the game to be about paying attention in the environment and exploring and being curious, that’s perfectly fine! And I might even enjoy it a lot, like I have for many games. But it fails at this. There are many games that do this so much better. You can make it fun. You can make it so exploration and trying to find the next thing is actually enjoyable and worthwhile. Nine Sols doesn’t even attempt this.</p>

<h1 id="its-not-me">It’s not me.</h1>

<p>I’m very comfortable admitting that I’m not the best at navigation. Neither in games nor in real life. I get lost very easily. Still, this game is just so awful in this regard as I just explained, and considering what I’ve heard from other people getting lost, I <em>know</em> that the problem I have with this game isn’t just about me not being good at navigation.</p>

<p>I have played many metroidvanias, and recently I’ve played a few games where exploration is their <em>main</em> thing, and I still loved the experience. There’s a chapter in <a href="https://store.steampowered.com/app/1262350/SIGNALIS/"><em>Signalis</em></a> where you <em>don’t even have a map!</em>, and its level design intentionally tries to disorient you. And I still loved it! Right now I’m playing <a href="https://store.steampowered.com/app/553420/TUNIC/">Tunic</a>, which is mainly about <em>the joy of discovery</em>. Maps are rare and you have to pay attention to decipher all the cryptic information given to you to be able to navigate its open ended map and make progress. And the game is absolutely beautiful in all aspects. I am loving every second of it.</p>

<p>I’m just trying to make the point that Nine Sols’s problem isn’t that it has exploration in it and I’m not into it. Nine Sols’s problem is that it does this very poorly, to the point of being <strong>hostile</strong>.</p>

<p>My time is better spent on games that actually try to not waste my time.</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="gaming" /><summary type="html"><![CDATA[Cute little guy I’ve been playing Nine Sols for about 13 hours and I think I won’t go back to it now. For an overview of the game: It’s a decent metroidvania game with great hand-drawn visuals and a unique setting. They call it “Taopunk”, it’s quite interesting and original. Apart from the visuals and setting, the game doesn’t stray too far from other games in this “metroidvania mixed with dark souls” genre. It’s serviceable. It plays it extremely safe. One of the main aspects of the combat is the parry mechanic. It’s a very overused mechanic recently in games, and from someone who has played Sekiro and loves it to death, and just beat Lies of P, the parry in this game is quite underwhelming. It just doesn’t feel very satisfying. Everything you do in combat doesn’t have a lot of impact, in fact. But it’s fine! It’s a perfectly serviceable combat system. I’m not writing this article because of these things. We’re gonna talk about why I dropped the game; and what has frustrated me so much time and time again while trying to make progress in this damn game. Hostile navigation What ruined my experience playing Nine Sols is that the game tries its best to get you lost. This has been the hardest game to navigate that I have played in recent memory. I ran around in circles for hours. It’s infuriating. The game doesn’t even give you a hint about where to go next. The map viewer isn’t very helpful. Most areas are indistinguishable from each other. The map is very open with little sense of structured progression, so whenever you complete an area, chances are you’ll be back to square one and have absolutely no idea where to go next. I’ll try to be as precise as I can explaining the problem: Progression within levels is good. The problem is when you finish a story beat and you’re done with your current area. Figuring out where you need to go next might take hours. What also happened in my playthrough is that sometimes when I seemed to have found the way, I explore, then after a while I realize that it’s an optional area and that I haven’t done any real progress, and I’m back at square one. I’ve been having these thoughts about the game’s navigation for a while. I talked about it with other people and most seemed to agree in some way. Then I looked online and I saw a lot of people begging for help on various forums. This is a real problem in the game. But what really prompted me to actually write about this and start my vendetta against this game is the thought that… they did this on purpose. No, of course they didn’t do it on purpose. Did they, though? Yes, the problem is so bad that you can’t help but wonder. It gives me the slight impression that they intentionally want the player to get lost constantly, to pad out the game’s runtime. Of course, I have no evidence to back up this theory, and I’m not sure if I actually believe it; but it doesn’t really matter. Either the game is intentionally obtuse about you finding your way to make the game longer without having to produce more content, or they think there’s nothing wrong with the navigational design in the game. Both options are equally bad. Whether intentional or not, the game’s exploration design leads to a frustrating experience where the player feels deliberately starved of much needed information. Failing where Hollow Knight succeeded For contrast, let’s talk about all the tools and information Hollow Knight gives you to navigate its giant open-ended world. It’s a very apt comparison as these games share the same “metroidvania/Dark Souls” genre. Each biome is quite distinct in style and in enemy variety. The way you acquire more maps and information within those maps has a clear progression, resulting in a clear understanding of what you already explored versus uncharted territory Gamified navigational tools, like you can buy pins that automatically mark different things on the map. You can also buy custom pins that you can place wherever you want. This lets you keep a personalized record of your travels and what you’ve obsersed and haven’t observed. It is very clear when you can’t proceed because you lack a certain ability. This could be a transition between biomes (easy to remember), or something very memorable and distinct, like a black flowing barrier. The map view is much easier to understand. Well defined areas with their unique shapes and landmarks Pre-placed markers and custom markers for the player help a lot The game has many more tools to orient the player that are more suble than this, but this is just what came to mind. Nine Sols lacks all of this. What else could they have done? I think I covered enough ideas in the Hollow Knight section, but here’s a couple more: NPC’s informing you of your current quest, and talking to you about locations. Anything that could give you a clue about where you are and where you’re going. Maybe new NPC’s could call you with clues, or taunting you towards the next area. Some kind of progression log. This could be not subtle at all, like a literal Quest Status section on the menu; or something in the Hub that informs you of your current progress, like maybe a canvas on a wall that is progressively filled. A better map view. Differentiate explored areas with unexplored areas. Tempt me with a blacked out area in the map that I haven’t gone to yet. Something. Just do something to help. Rebutting some counter points “The AI guy tells you where to go” There’s a robot/AI furry person/thing inside your hub. Sometimes (very rarely), it will tell you something that could be considered “useful”. But: The AI guy will very rarely tell you where to go. If it does tell you, it won’t be very specific. If it does tell you, it will never tell you again. It will only tell you once. Subsequent dialogue prompts will be about something useless. That means that if you accidentally skipped that line, or you forgot what it said, there is no way to get that information inside of the game. Why would they make important information like that so easily missable? “Sometimes, the game marks the map for where to go next” Yes, but it does this so, so, so rarely. Sometimes, the game will point towards a dead end. This has actually happened to me. It pointed me somewhere, but at the end of that area, I needed a key to proceed. Where do you get the key? Good luck with that! Just like the previous point, they only mark it on the map once. After you saw it for the first time, the marker disappears forever. Why? Why would they do this? The overview map isn’t very useful… It’s pretty, though! “It’s about exploring past areas until you find the next thing! You have to take it slow!” This isn’t good enough for me anymore. I have much less tolerance for a game wasting my time than when I was younger. This just doesn’t cut it anymore. If you want the game to be about paying attention in the environment and exploring and being curious, that’s perfectly fine! And I might even enjoy it a lot, like I have for many games. But it fails at this. There are many games that do this so much better. You can make it fun. You can make it so exploration and trying to find the next thing is actually enjoyable and worthwhile. Nine Sols doesn’t even attempt this. It’s not me. I’m very comfortable admitting that I’m not the best at navigation. Neither in games nor in real life. I get lost very easily. Still, this game is just so awful in this regard as I just explained, and considering what I’ve heard from other people getting lost, I know that the problem I have with this game isn’t just about me not being good at navigation. I have played many metroidvanias, and recently I’ve played a few games where exploration is their main thing, and I still loved the experience. There’s a chapter in Signalis where you don’t even have a map!, and its level design intentionally tries to disorient you. And I still loved it! Right now I’m playing Tunic, which is mainly about the joy of discovery. Maps are rare and you have to pay attention to decipher all the cryptic information given to you to be able to navigate its open ended map and make progress. And the game is absolutely beautiful in all aspects. I am loving every second of it. I’m just trying to make the point that Nine Sols’s problem isn’t that it has exploration in it and I’m not into it. Nine Sols’s problem is that it does this very poorly, to the point of being hostile. My time is better spent on games that actually try to not waste my time.]]></summary></entry><entry><title type="html">LucyNES - I made an emulator in Odin!</title><link href="/blog/lucynes-update.html" rel="alternate" type="text/html" title="LucyNES - I made an emulator in Odin!" /><published>2025-02-10T00:00:00+00:00</published><updated>2025-02-10T00:00:00+00:00</updated><id>/blog/lucynes</id><content type="html" xml:base="/blog/lucynes-update.html"><![CDATA[<p>I added a new entry to my showcase page! <a href="../lucynes.html">Here it is</a>! It’s about my experience making a NES emulator in Odin. You can read all about it there.</p>

<p>Now, I’m learning a bit about Godot and 3D character animations; so I can maybe make something cool with them :smile:</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="programming" /><category term="odin" /><summary type="html"><![CDATA[I added a new entry to my showcase page! Here it is! It’s about my experience making a NES emulator in Odin. You can read all about it there. Now, I’m learning a bit about Godot and 3D character animations; so I can maybe make something cool with them :smile:]]></summary></entry><entry><title type="html">My Gaming Log</title><link href="/blog/gaming-log.html" rel="alternate" type="text/html" title="My Gaming Log" /><published>2024-02-27T00:00:00+00:00</published><updated>2024-02-27T00:00:00+00:00</updated><id>/blog/gaming-log</id><content type="html" xml:base="/blog/gaming-log.html"><![CDATA[<p>This is my log for most of the games I play.</p>

<p>Also, check out my <a href="https://steamcommunity.com/id/peropero__/recommended/">reviews on Steam</a></p>

<p>(WIP) I have to fill this with more games. I have them logged somewhere else. It’s a matter of moving what’s there to here.</p>

<h3 id="2023-12-07---the-legend-of-zelda---echoes-of-wisdom">2023-12-07 - The Legend of Zelda - Echoes of Wisdom</h3>

<p>(beaten in 20hs)</p>

<p>I loved the new mechanics in this Zelda game. It’s a very adorable and comfy game to play with a lot of puzzles, and with a new twist to the gameplay. Only downsides are: It’s too long, it gets too easy, and it doesn’t go deep enough with its own mechanics, after a certain point in the game.</p>

<h3 id="2024-12-23---inscryption">2024-12-23 - Inscryption</h3>

<p>(beaten in 15hs)</p>

<p>An outstanding game. I am speechless. It rises above other indie games. It’s very immersive and it manages to mix multiple game genres, all tied to an exciting plot. It may be my personal 2024 GOTY.</p>

<h3 id="2025-01-07---bloodstained">2025-01-07 - Bloodstained</h3>

<p>(beaten in 17.1hs)</p>

<p>It’s basically Castlevania - Symphony of the Night, but more modern. Lots of weapons to use. I enjoyed it a lot, but it was too long for me, for what it is. I had to force myself to beat it.
I got lost at times and I didn’t enjoy that. I resorted to look up online where to go. But that’s on me, I’m not a big fan of exploration.
It’s goofy and sometimes rough around the edges, but it has its charm. It has the “indie feel”. Some bosses felt unfair. There are some difficulty spikes that are tough to cross, but that’s OK.
David Hayter is in this game!! I loved that.</p>

<h3 id="2025-01-25---buckshot-roulette">2025-01-25 - Buckshot Roulette</h3>

<p>(beaten in 30m)
Small game I enjoyed a lot. Packed with style and it made me strategize a bit. Made in Godot!</p>

<h3 id="2025-02-01---curse-of-the-dead-gods">2025-02-01 - Curse of the Dead Gods</h3>

<p>(beaten in 24hs)</p>

<p>Very addictive roguelike with solid combat. The visual style is something that appealed to me a lot too, with dark dungeons, and the lighting. All the combat mechanics work very well with each other. There is a lot of weapon variety so it was easy to find weapons that I found fun. It’s very rewarding when you beat dungeons. There’s a lot of old school Diablo vibes. It’s very good.</p>

<p>Context: I could not get into Hades and I tried a lot. I prefer this one much more.</p>

<p>I just beat the game (well, the standard exploration tree). It’s been very challenging but very rewarding. This game tested my limits but in a good way. It took a lot of perseverance, patience, and focus. And it felt great at the end. Try this game out if you like a good challenge.</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="gaming" /><summary type="html"><![CDATA[This is my log for most of the games I play. Also, check out my reviews on Steam (WIP) I have to fill this with more games. I have them logged somewhere else. It’s a matter of moving what’s there to here. 2023-12-07 - The Legend of Zelda - Echoes of Wisdom (beaten in 20hs) I loved the new mechanics in this Zelda game. It’s a very adorable and comfy game to play with a lot of puzzles, and with a new twist to the gameplay. Only downsides are: It’s too long, it gets too easy, and it doesn’t go deep enough with its own mechanics, after a certain point in the game. 2024-12-23 - Inscryption (beaten in 15hs) An outstanding game. I am speechless. It rises above other indie games. It’s very immersive and it manages to mix multiple game genres, all tied to an exciting plot. It may be my personal 2024 GOTY. 2025-01-07 - Bloodstained (beaten in 17.1hs) It’s basically Castlevania - Symphony of the Night, but more modern. Lots of weapons to use. I enjoyed it a lot, but it was too long for me, for what it is. I had to force myself to beat it. I got lost at times and I didn’t enjoy that. I resorted to look up online where to go. But that’s on me, I’m not a big fan of exploration. It’s goofy and sometimes rough around the edges, but it has its charm. It has the “indie feel”. Some bosses felt unfair. There are some difficulty spikes that are tough to cross, but that’s OK. David Hayter is in this game!! I loved that. 2025-01-25 - Buckshot Roulette (beaten in 30m) Small game I enjoyed a lot. Packed with style and it made me strategize a bit. Made in Godot! 2025-02-01 - Curse of the Dead Gods (beaten in 24hs) Very addictive roguelike with solid combat. The visual style is something that appealed to me a lot too, with dark dungeons, and the lighting. All the combat mechanics work very well with each other. There is a lot of weapon variety so it was easy to find weapons that I found fun. It’s very rewarding when you beat dungeons. There’s a lot of old school Diablo vibes. It’s very good. Context: I could not get into Hades and I tried a lot. I prefer this one much more. I just beat the game (well, the standard exploration tree). It’s been very challenging but very rewarding. This game tested my limits but in a good way. It took a lot of perseverance, patience, and focus. And it felt great at the end. Try this game out if you like a good challenge.]]></summary></entry><entry><title type="html">Game Dev Update - Getting into Godot!</title><link href="/blog/godot-update.html" rel="alternate" type="text/html" title="Game Dev Update - Getting into Godot!" /><published>2024-02-27T00:00:00+00:00</published><updated>2024-02-27T00:00:00+00:00</updated><id>/blog/getting-into-godot</id><content type="html" xml:base="/blog/godot-update.html"><![CDATA[<p>I’m going to document a bit about what I’ve been doing since releasing <a href="/psycho-box.html">Psycho Box</a>.</p>

<p>After Psycho Box, and even while making Psycho Box, I kept wondering how faster I would be making games if I were using an engine, assuming I were good at using such engine.
Don’t get me wrong, I’m no stranger to game engines. I’ve tried a lot of them in the past. But it’s been a while. So my next goal was set: Learn Godot and make games in it.</p>

<h2 id="my-godot-adventures">My Godot Adventures</h2>

<p>I’ve known about Godot for a long time. There’s a personal history between me and Godot. It originated in Argentina, where I live, and I even had a job that was primarily about Godot.
But even ignoring all that, I like Godot because it’s relatively lightweight, fully open source, and malleable. It’s so malleable that you get to pick which programming language to use for gameplay code. You can technically use C++ to code your entire game, but admittedly it’s not very practical. So the two main options for scripting are GDScript and C#.</p>

<p>So that’s what I’ve been doing mainly on my spare time, after releasing Psycho Box: Making stuff in Godot, mainly to get better at it. My first (failed) project was to clone an old Flash game I liked: <a href="https://www.youtube.com/watch?v=hYJ4EeE5hZ4">Sports Heads</a>. But even that proved too hard a task for me. I couldn’t get the physics right. It was quite frustrating. So I decided to quit that project.</p>

<h3 id="pew-pew-baba">Pew Pew Baba</h3>

<p>After that, I decided to make the simplest game I could think of. A shoot-em-up, but with mobile controls, and endless. You only use two buttons to play the game: You can turn left, or turn right. You can be in one of three lanes. All you have to do is not get hit.</p>

<p>It turned out to be a great idea, because I managed to finish the project, and it was fun to make. Also, the game can be quite fun. Some people liked it, at least! Here’s a link to the <a href="/pew-pew-baba.html">Pew Pew Baba article</a>. The game is on itch.io, if you wanna play it.</p>

<iframe frameborder="0" src="https://itch.io/embed/2539667?linkback=true&amp;dark=true" width="552" height="167"><a href="https://lucypero.itch.io/pew-pew-baba">Pew Pew Baba by Lucy</a></iframe>

<p>While making this tiny game, I learned a lot about basic things in Godot. So it was a great experience. I also learned a bit about how to make SFX, and a bit about music. My boyfriend taught me about sound, and he’s the one who made the amazing
music track in the game! 🙂</p>

<h3 id="after-pew-pew-baba---c">After Pew Pew Baba - C#</h3>

<p>This is where things started slowing down. I started losing focus in my game dev activities, so I haven’t made much progress in any project or in my game dev education.</p>

<p>Even before finishing Pew Pew Baba, I was interested in coding in C# instead of GDScript. So I slowly started doing it. I partially ported Pew Pew Baba to C#, then I started other projects in C#.
There’s nothing wrong with GDScript. In fact, it’s a really good dynamic scripting language. It’s probably my favorite one. But I’ll always be partial to statically typed languages. It will always feel better
to know that if the program compiles, then you are assured that there are no obvious mistakes in the code which will make the game crash at any moment. It also makes refactoring much easier, and stress-free.</p>

<p>So yeah, I’ve been learning a lot of C# lately. The language borrows a lot from C++, but it has its own quirks that you have to know about. For example, the difference between <code class="language-plaintext highlighter-rouge">struct</code> and <code class="language-plaintext highlighter-rouge">class</code> in C# is much more substantial than in C++.
I have used C# in the past in Unity, but that was years ago, so I had to re-learn many things. But I was up and running in no time.</p>

<h3 id="my-jrpg-project">My JRPG project</h3>

<p>I decided my next project to be a JRPG. I was never fully committed to it, I still am not, so maybe that’s why I’ve been slowing down so much.</p>

<p>I was interested in learning how to code and structure the code and data of a JRPG (Japanese Role Playing Game). So this project is mainly for a challenge, and for technical curiosity. I don’t think I am capable of making an actual JRPG that is remotely fun or interesting by myself. Not at this moment, at least.</p>

<p>So yeah, I even wrote some design documents about what the game will be. Honestly I didn’t put much time into it, and I didn’t make much progress. I’m kinda in a slump right now when it comes to my game dev adventure.</p>

<p>Might as well show a little bit of what I managed to put together. I started this project as a 3D/2D game, where the environments are in 3D and the characters are 2D. Then, I coded a basic turn-based combat system. It was challenging but eventually I managed. It’s pretty cool, imo. Here’s a video where the player is in the overworld, initiates a battle, then ends the battle and back to the overworld:</p>

<video controls="">
  <source src="/assets/videos/jrpg-3d-but-2d.mp4" type="video/mp4" />
</video>

<p>Then I decided to go back to 2D, to make it easier for myself. I started working on the Overworld part of the game. Here’s you talking to NPC’s:</p>

<video controls="">
  <source src="/assets/videos/jrpg-talk.mp4" type="video/mp4" />
</video>

<p>You get the idea. Nothing that is well developed. I haven’t put in the time needed for this project.</p>

<h3 id="closing-thoughts">Closing Thoughts</h3>

<p>Anyway, I am low on motivation lately. I hope I can find a hook that can bring me back at full energy. Whether it’s in this JRPG project, or another. I will not totally commit to this project yet. If I have to drop it, I will. Because maybe it’s
not the right project I should be working on right now.</p>

<p>There’s a lot of technical challenges that I’d still love to tackle in this genre. Like how to store branching dialogue in a game like this, or how to store data about items and stats. It’s interesting.</p>

<p>Anyway, here’s where I’m at right now. It’s not the best place to be, but that’s how it is sometimes. I thought that it’d be cool to make the overworld in first person 3D, while being in a dungeon, ala Legend of Grimrock. So maybe I’ll explore that.</p>

<p>Also, I’m interested in learning more specific areas of game development, rather than making a complete game. For example, I want to learn about character animation. To this day, I don’t know much about that, and I feel like I should.
Also, I wanna get more into shaders, and 3D asset creation.</p>

<p>There’s so much to learn. 😊</p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="game-dev" /><category term="godot" /><summary type="html"><![CDATA[I’m going to document a bit about what I’ve been doing since releasing Psycho Box. After Psycho Box, and even while making Psycho Box, I kept wondering how faster I would be making games if I were using an engine, assuming I were good at using such engine. Don’t get me wrong, I’m no stranger to game engines. I’ve tried a lot of them in the past. But it’s been a while. So my next goal was set: Learn Godot and make games in it. My Godot Adventures I’ve known about Godot for a long time. There’s a personal history between me and Godot. It originated in Argentina, where I live, and I even had a job that was primarily about Godot. But even ignoring all that, I like Godot because it’s relatively lightweight, fully open source, and malleable. It’s so malleable that you get to pick which programming language to use for gameplay code. You can technically use C++ to code your entire game, but admittedly it’s not very practical. So the two main options for scripting are GDScript and C#. So that’s what I’ve been doing mainly on my spare time, after releasing Psycho Box: Making stuff in Godot, mainly to get better at it. My first (failed) project was to clone an old Flash game I liked: Sports Heads. But even that proved too hard a task for me. I couldn’t get the physics right. It was quite frustrating. So I decided to quit that project. Pew Pew Baba After that, I decided to make the simplest game I could think of. A shoot-em-up, but with mobile controls, and endless. You only use two buttons to play the game: You can turn left, or turn right. You can be in one of three lanes. All you have to do is not get hit. It turned out to be a great idea, because I managed to finish the project, and it was fun to make. Also, the game can be quite fun. Some people liked it, at least! Here’s a link to the Pew Pew Baba article. The game is on itch.io, if you wanna play it. Pew Pew Baba by Lucy While making this tiny game, I learned a lot about basic things in Godot. So it was a great experience. I also learned a bit about how to make SFX, and a bit about music. My boyfriend taught me about sound, and he’s the one who made the amazing music track in the game! 🙂 After Pew Pew Baba - C# This is where things started slowing down. I started losing focus in my game dev activities, so I haven’t made much progress in any project or in my game dev education. Even before finishing Pew Pew Baba, I was interested in coding in C# instead of GDScript. So I slowly started doing it. I partially ported Pew Pew Baba to C#, then I started other projects in C#. There’s nothing wrong with GDScript. In fact, it’s a really good dynamic scripting language. It’s probably my favorite one. But I’ll always be partial to statically typed languages. It will always feel better to know that if the program compiles, then you are assured that there are no obvious mistakes in the code which will make the game crash at any moment. It also makes refactoring much easier, and stress-free. So yeah, I’ve been learning a lot of C# lately. The language borrows a lot from C++, but it has its own quirks that you have to know about. For example, the difference between struct and class in C# is much more substantial than in C++. I have used C# in the past in Unity, but that was years ago, so I had to re-learn many things. But I was up and running in no time. My JRPG project I decided my next project to be a JRPG. I was never fully committed to it, I still am not, so maybe that’s why I’ve been slowing down so much. I was interested in learning how to code and structure the code and data of a JRPG (Japanese Role Playing Game). So this project is mainly for a challenge, and for technical curiosity. I don’t think I am capable of making an actual JRPG that is remotely fun or interesting by myself. Not at this moment, at least. So yeah, I even wrote some design documents about what the game will be. Honestly I didn’t put much time into it, and I didn’t make much progress. I’m kinda in a slump right now when it comes to my game dev adventure. Might as well show a little bit of what I managed to put together. I started this project as a 3D/2D game, where the environments are in 3D and the characters are 2D. Then, I coded a basic turn-based combat system. It was challenging but eventually I managed. It’s pretty cool, imo. Here’s a video where the player is in the overworld, initiates a battle, then ends the battle and back to the overworld: Then I decided to go back to 2D, to make it easier for myself. I started working on the Overworld part of the game. Here’s you talking to NPC’s: You get the idea. Nothing that is well developed. I haven’t put in the time needed for this project. Closing Thoughts Anyway, I am low on motivation lately. I hope I can find a hook that can bring me back at full energy. Whether it’s in this JRPG project, or another. I will not totally commit to this project yet. If I have to drop it, I will. Because maybe it’s not the right project I should be working on right now. There’s a lot of technical challenges that I’d still love to tackle in this genre. Like how to store branching dialogue in a game like this, or how to store data about items and stats. It’s interesting. Anyway, here’s where I’m at right now. It’s not the best place to be, but that’s how it is sometimes. I thought that it’d be cool to make the overworld in first person 3D, while being in a dungeon, ala Legend of Grimrock. So maybe I’ll explore that. Also, I’m interested in learning more specific areas of game development, rather than making a complete game. For example, I want to learn about character animation. To this day, I don’t know much about that, and I feel like I should. Also, I wanna get more into shaders, and 3D asset creation. There’s so much to learn. 😊]]></summary></entry><entry><title type="html">My First 3D Game - Update 3 - The game is out!</title><link href="/blog/3d-game-update-3.html" rel="alternate" type="text/html" title="My First 3D Game - Update 3 - The game is out!" /><published>2023-11-21T00:00:00+00:00</published><updated>2023-11-21T00:00:00+00:00</updated><id>/blog/game-update-release</id><content type="html" xml:base="/blog/3d-game-update-3.html"><![CDATA[<p>This is a follow-up to <a href="./3d-game-update-2.html">game update 2</a></p>

<p>The game is out!! Unbelievable. It’s done. Play it! Here it is, it’s free. Oh, and it’s called <strong>Psycho Box</strong>:</p>

<iframe frameborder="0" src="https://itch.io/embed/2366585?dark=true" width="552" height="167"><a href="https://lucypero.itch.io/psycho-box">Psycho Box by Lucy</a></iframe>

<p>I am very happy with this entire experience, but my favorite part is knowing that people played my game and enjoyed it. I am happy with the download and view stats that the game got these last 2 days. 113 views and 21 downloads. Considering that this project was just for self education, I am more than happy with it.</p>

<p>I’m gonna talk about how the last 2 weeks of development were, what I learned, and my next steps.</p>

<h2 id="level-production">Level Production</h2>

<p>The main thing I had to do before shipping the game was to make levels. I was worried because I never made game levels before, and I was struggling at first. However, I was able to loosen up and start being creative, and I was able to make 20 levels that are at least playable.</p>

<p>The tool I used to make levels was <a href="https://ldtk.io/">Ldtk</a>. I really like this tool. It’s been essential to quickly make levels and try them out. I wrote a small python script to import the levels to my game’s internal format, and also I implemented hot-loading in my game. This way I could immediately try out the level in my game without re-compiling it and re-launching it. This fast workflow was essential for quick level production.</p>

<h2 id="what-i-learned">What I learned</h2>

<p>I learned all sorts of things. Some things were technical: about programming in general, about 3D rendering, and other niche specific knowledge about C++, build systems, Win32, DX11 and other things. But I don’t want to focus on that right now. I learned other important skills that are unrelated to programming: How to finish and deliver a game, the basics of level design, and game design. I will treasure what I learned and use this as a jumping off point to make bigger and better games.</p>

<h2 id="moving-forward">Moving forward</h2>

<p>I will not abandon Psycho Box right away. I will fix basic things, and add some low-hanging fruit features, like controller support.</p>

<p>I will also make the source code public, so I’m gonna have to rewrite some parts out of embarrassment, but nothing major 😛</p>

<p>After Psycho Box I plan to re-learn Godot and get good at it, so I can prototype games much quicker than I did with this game. I’m excited about what the future might bring.</p>

<h2 id="update">Update</h2>

<p>Here’s the <a href="https://github.com/lucypero/psycho-box">source code</a></p>

<p>And the <a href="../psycho-box.html">project article</a></p>]]></content><author><name>Lucy</name><email>lucyperopero@gmail.com</email></author><category term="game-dev" /><category term="programming" /><category term="c++" /><summary type="html"><![CDATA[This is a follow-up to game update 2 The game is out!! Unbelievable. It’s done. Play it! Here it is, it’s free. Oh, and it’s called Psycho Box: Psycho Box by Lucy I am very happy with this entire experience, but my favorite part is knowing that people played my game and enjoyed it. I am happy with the download and view stats that the game got these last 2 days. 113 views and 21 downloads. Considering that this project was just for self education, I am more than happy with it. I’m gonna talk about how the last 2 weeks of development were, what I learned, and my next steps. Level Production The main thing I had to do before shipping the game was to make levels. I was worried because I never made game levels before, and I was struggling at first. However, I was able to loosen up and start being creative, and I was able to make 20 levels that are at least playable. The tool I used to make levels was Ldtk. I really like this tool. It’s been essential to quickly make levels and try them out. I wrote a small python script to import the levels to my game’s internal format, and also I implemented hot-loading in my game. This way I could immediately try out the level in my game without re-compiling it and re-launching it. This fast workflow was essential for quick level production. What I learned I learned all sorts of things. Some things were technical: about programming in general, about 3D rendering, and other niche specific knowledge about C++, build systems, Win32, DX11 and other things. But I don’t want to focus on that right now. I learned other important skills that are unrelated to programming: How to finish and deliver a game, the basics of level design, and game design. I will treasure what I learned and use this as a jumping off point to make bigger and better games. Moving forward I will not abandon Psycho Box right away. I will fix basic things, and add some low-hanging fruit features, like controller support. I will also make the source code public, so I’m gonna have to rewrite some parts out of embarrassment, but nothing major 😛 After Psycho Box I plan to re-learn Godot and get good at it, so I can prototype games much quicker than I did with this game. I’m excited about what the future might bring. Update Here’s the source code And the project article]]></summary></entry></feed>