<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Scarlet Engineering - Devlog</title>
  <subtitle>Game development devlog from Scarlet Engineering</subtitle>
  <link href="https://scarlet.engineering/blog/feed.xml" rel="self"/>
  <link href="https://scarlet.engineering/blog/"/>
  <updated>2026-04-10T00:00:00Z</updated>
  <id>https://scarlet.engineering/blog/</id>
  <author>
    <name>Scarlet Engineering</name>
  </author>
  
  <entry>
    <title>Curves of Fun</title>
    <link href="https://scarlet.engineering/blog/curves-of-fun/"/>
    <updated>2026-02-14T00:00:00Z</updated>
    <id>https://scarlet.engineering/blog/curves-of-fun/</id>
    <content type="html">&lt;p&gt;Notes of &lt;a href=&quot;https://www.amazon.com/Advanced-Game-Design-Systems-Approach/dp/0134667603&quot;&gt;Advanced Game Design: A Systems Approach&lt;/a&gt; by Michael Sellers.&lt;/p&gt;
&lt;p&gt;Charts are made with Python and Matplotlib.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Games need goals. and the journey of achieving the goals needs to be fun.&lt;/p&gt;
&lt;h2&gt;The Goal&lt;/h2&gt;
&lt;p&gt;The goal, e.g. dps required, xp required to level up, etc... is usually an exponential function of the level. They make early levels seem attainable and players go through them fast, while later ones seem at first to be so far off numerically as to almost require superhuamn abilities to reach.&lt;/p&gt;
&lt;p&gt;By the time the players start reaching these levels, however, they are also gaining astronomical numbers of experience points, so they feel a continuing sense of achievement in both their individual victories (the numeric rewards do not seem paltry) and in their overall number of experience points gained.&lt;/p&gt;
&lt;p&gt;Usually we can just straight up copy the OSRS&#39;s exp formula:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E(L) = ⌊(1/4) × Σ(x=1 to L−1) ⌊x + 300 × 2^(x/7)⌋⌋&lt;/strong&gt;,&lt;/p&gt;
&lt;p&gt;which is an exponential function with base ~= 1.1041.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/osrs_exp.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The fun is how the player reach the goal&lt;/h2&gt;
&lt;p&gt;The player&#39;s power/efficiency increase is usually a quadratic polynomial. The gap the goal and the efficiency is where the tension comes from: every level gets a bit harder to reach, but the player also gets more powerful. The player is always making progress, but the goal is always just out of reach.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/progression.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, if we only have one polynomial, then there&#39;s no decision to be made: the player just keeps doing the same thing over and over again, and the game becomes boring.&lt;/p&gt;
&lt;p&gt;Sigmoid/hyperbolic scaling functions comes in handy. They create sub-goals. Each progression method(a level up zone, a crafting recipe, skill unlocks, quest tiers, etc...) can be designed to be the most efficient way to progress for a certain range of levels. The gameplay now have rhythm and variety, and the player can choose which method to use based on their preferences.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/sigmoid.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;They&#39;re also a powerful tools for narrative design. Mastering a sub-goal (e.g. a crafting recipe) can provides an additonal bonus to the player. QOL, aesthetic,unlock new content, etc... They naturally open new decisions for the player and expand the world.&lt;/p&gt;
&lt;p&gt;Maybe there&#39;s a grandmaster quest that requires multiple goals to be reached to solve. or the player needs a certain level of crafting to unlock a new area. The new area needs a certain level of combat to survive. Fighting monsters in the new area gives you a rare materials that can be used for more efficient weapon-smithing... etc. They&#39;re all interlinked, and the player&#39;s decisions on which sub-goal to pursue will affect their overall progression and experience in the game.&lt;/p&gt;
&lt;h2&gt;Analyzing the curves&lt;/h2&gt;
&lt;p&gt;The curves are also helpful for seeing the overall balance of the progression of the game.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/analyze.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can see the pacing of the game, the difficulty spikes, the efficiency of different methods, etc... Is a new sigmoid needed for level 30-50 because there&#39;s no meaningful decision to be made in that range? Is the early game too easy because the player can just grind one method for a long time? Are there any bottlenecks that prevent the player from reaching the later levels?&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;All of these are over-simplifications, of course. Pretty handy tho.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Murderhorn</title>
    <link href="https://scarlet.engineering/blog/murderhorn/"/>
    <updated>2026-02-20T00:00:00Z</updated>
    <id>https://scarlet.engineering/blog/murderhorn/</id>
    <content type="html">&lt;p&gt;I&#39;m a big fan of short incremental games with cool narratives. Games like &lt;a href=&quot;https://store.steampowered.com/app/3361470/Digseum/&quot;&gt;Digseum&lt;/a&gt; and &lt;a href=&quot;https://jontopielski.itch.io/1000000-shrimp&quot;&gt;1,000,000 Shrimps&lt;/a&gt; are delightful and memorable.&lt;/p&gt;
&lt;p&gt;I made a game that&#39;s heavily inspried by them. Try it here:&lt;/p&gt;
&lt;iframe frameborder=&quot;0&quot; src=&quot;https://itch.io/embed/4306840&quot; width=&quot;552&quot; height=&quot;167&quot;&gt;&lt;a href=&quot;https://devpoga.itch.io/murderhorn&quot;&gt;Murderhorn by Poga&lt;/a&gt;&lt;/iframe&gt;
&lt;hr /&gt;
&lt;p&gt;Last week I did my first hiking with my wife. During the trip I keep thinking about an old saying: &amp;quot;You cannot speak of ice to a summer insect&amp;quot;(夏蟲不可語冰). How would a mountain feel when there&#39;s all these short-lived humans walking on top if it? Probably nothing.&lt;/p&gt;
&lt;p&gt;Hence the game. It&#39;s deliberately short. Because we&#39;re all just summer insects.&lt;/p&gt;
&lt;p&gt;Hope you enjoyed it.
&lt;br /&gt;Poga&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>One Shader, One Mesh, One Island</title>
    <link href="https://scarlet.engineering/blog/ground-shader/"/>
    <updated>2026-04-10T00:00:00Z</updated>
    <id>https://scarlet.engineering/blog/ground-shader/</id>
    <content type="html">&lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; style=&quot;max-width: 100%; height: auto;&quot;&gt;
  &lt;source src=&quot;https://scarlet.engineering/blog/images/ground_hero.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;This is one shader on a flat mesh. No hand-painted terrain, no tile maps. Just math. Here&#39;s how it works.&lt;/p&gt;
&lt;pre style=&quot;font-size: 0.75em; line-height: 1.4; overflow-x: auto;&quot;&gt;
Polygon Regions (water, meadow)
        │
        ▼
┌─────────────────┐
│  GPU SDF Baking  │  Each polygon → SubViewport → signed distance field
└────────┬────────┘
         ▼
┌─────────────────┐
│  Pack into RGBA  │  R,G,B = water regions │ A = meadow
└────────┬────────┘
         ▼
┌─────────────────┐
│  Zone Blending   │  SDF distance → smoothstep → ground/meadow/forest
└────────┬────────┘
         ▼
┌─────────────────┐
│  Water Overlay   │  FBM-distorted shoreline → foam/shallow/medium/deep
└────────┬────────┘
         ▼
┌─────────────────┐
│  Tidal Animation │  lerp(low SDF, high SDF, tide) + per-point wobble
└─────────────────┘
&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;It&#39;s All SDFs&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/ground_sdf_viz.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The trick behind everything is a &lt;strong&gt;signed distance field&lt;/strong&gt;. For every pixel on the ground, we bake a single number: how far is this point from the nearest coastline? Negative means underwater, positive means land.&lt;/p&gt;
&lt;p&gt;That one number drives the entire shader — where the water is, where foam appears, how deep the sea gets, where biomes blend.&lt;/p&gt;
&lt;p&gt;We bake the SDFs on the GPU at map generation time. Each water region&#39;s polygon gets rendered into a SubViewport using a shader that computes the signed distance from every pixel to that polygon&#39;s edges. Then we pack up to three regions into the R, G, B channels of a single texture, with meadow distance in the alpha channel. One texture lookup, four distance fields.&lt;/p&gt;
&lt;h2&gt;Painting the Ground&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/ground_zones.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;With the SDF in hand, blending terrain textures is just a &lt;code&gt;smoothstep&lt;/code&gt; on the distance. Close to a forest region? Blend in forest texture. Inside a meadow area? Fade to meadow. The transitions are soft and organic — no hard seams, no manual painting.&lt;/p&gt;
&lt;p&gt;The SDF does all the spatial reasoning. The shader just asks &amp;quot;how close am I?&amp;quot; and blends accordingly.&lt;/p&gt;
&lt;h2&gt;The Water&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://scarlet.engineering/blog/images/ground_water.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The shoreline is where it gets fun. We layer four zones by depth: foam at the very edge, then shallow water, medium sea, and deep sea. Each has its own texture, its own color variation, and its own shimmer — subtle FBM noise that shifts over time.&lt;/p&gt;
&lt;p&gt;But the real trick is that we don&#39;t draw the water boundary where the SDF says it is. We distort it with FBM noise first. That&#39;s what makes the coastline look organic instead of following the exact polygon edge. The water seeps into coves and pulls back from headlands, like a real shoreline.&lt;/p&gt;
&lt;h2&gt;Breathing Tides&lt;/h2&gt;
&lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; style=&quot;max-width: 100%; height: auto;&quot;&gt;
  &lt;source src=&quot;https://scarlet.engineering/blog/images/ground_hero.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;We actually bake &lt;em&gt;two&lt;/em&gt; SDF textures — one for low tide, one for high tide. Each represents a different polygon shape (the high-tide polygon is larger, eating into the land). At runtime, the shader just lerps between the two based on a tide level value.&lt;/p&gt;
&lt;p&gt;Each water region has its own tide level, so different coastlines can flood independently. And on top of the global tide, every point on the shoreline gets its own wobble phase from FBM noise. The water doesn&#39;t advance as a uniform wall — it laps and breathes unevenly, like waves on a real beach.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Pretty happy with how this turned out. One flat mesh, a couple of baked textures, and a lot of noise functions go a surprisingly long way.&lt;/p&gt;
</content>
  </entry>
</feed>
