<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://liuyisi.com/game-dev-notes</id>
    <title>Yisi Liu Blog</title>
    <updated>2026-10-26T03:51:51.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://liuyisi.com/game-dev-notes"/>
    <subtitle>Yisi Liu Blog</subtitle>
    <icon>https://liuyisi.com/img/favicon.png</icon>
    <entry>
        <title type="html"><![CDATA[Designing Whisker 2: From First Click to Final Level]]></title>
        <id>https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER</id>
        <link href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[Designing a game isn't just about individual mechanics. It's about crafting the entire experience from the moment someone clicks "Play" to when they finish the last level.]]></summary>
        <content type="html"><![CDATA[<p>Designing a game isn't just about individual mechanics. It's about crafting the entire experience from the moment someone clicks "Play" to when they finish the last level.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-first-30-seconds">The First 30 Seconds<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#the-first-30-seconds" class="hash-link" aria-label="Direct link to The First 30 Seconds" title="Direct link to The First 30 Seconds" translate="no">​</a></h2>
<p>The first impression matters more than I thought.</p>
<p>When players launch Whisker 2, they see:</p>
<ol>
<li class=""><strong>Title screen</strong> (clean, cozy aesthetic, soft music)</li>
<li class=""><strong>"Start" button</strong> (big, obvious, can't miss it)</li>
<li class=""><strong>Fade to Level 1</strong> (no long loading screen)</li>
</ol>
<p>I learned from Murder Mystery that complicated menus lose players. Whisker 2's start screen has 3 buttons total:</p>
<ul>
<li class="">Start</li>
<li class="">Settings</li>
<li class="">Credits</li>
</ul>
<p>That's it. No achievements menu. No stats. No tutorial select. Just "Start" and you're playing.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-tutorial-problem">The Tutorial Problem<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#the-tutorial-problem" class="hash-link" aria-label="Direct link to The Tutorial Problem" title="Direct link to The Tutorial Problem" translate="no">​</a></h2>
<p>I hate tutorial levels. They're boring and players skip them anyway.</p>
<p>But I also playtested with people who had never played Whisker 1, and they were completely lost without explanation.</p>
<p>My solution: <strong>The first level IS the tutorial, but it doesn't feel like one.</strong></p>
<p>Level 1 design:</p>
<ul>
<li class="">Only 2 customers</li>
<li class="">Only 1 recipe to learn (sandwich)</li>
<li class="">Only 3 ingredients visible</li>
<li class="">Customer patience timer is super generous (90 seconds)</li>
<li class="">Visual hints appear contextually</li>
</ul>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Tutorial hints appear based on player action, not forced dialogue</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playerNearIngredient </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">hasPickedUpIngredient</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">ShowHint</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Click to pick up ingredient"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playerHasIngredient </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> playerNearCustomer </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">hasServedCustomer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">ShowHint</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Click customer to serve"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Players learn by doing, not by reading text boxes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-difficulty-curve">The Difficulty Curve<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#the-difficulty-curve" class="hash-link" aria-label="Direct link to The Difficulty Curve" title="Direct link to The Difficulty Curve" translate="no">​</a></h2>
<p>This was the hardest part to balance.</p>
<p>Level progression in Whisker 2:</p>
<table><thead><tr><th>Level</th><th>Customers</th><th>Recipes</th><th>Time Pressure</th><th>New Mechanic</th></tr></thead><tbody><tr><td>1</td><td>2</td><td>1</td><td>Low</td><td>None (Tutorial)</td></tr><tr><td>2</td><td>3</td><td>2</td><td>Low</td><td>Oven introduced</td></tr><tr><td>3</td><td>4</td><td>2</td><td>Medium</td><td>None (Practice)</td></tr><tr><td>4</td><td>5</td><td>3</td><td>Medium</td><td>Combos introduced</td></tr><tr><td>5</td><td>6</td><td>3</td><td>High</td><td>Multiple orders</td></tr><tr><td>6</td><td>7</td><td>4</td><td>High</td><td>Everything combined</td></tr></tbody></table>
<p><strong>The pattern:</strong></p>
<ul>
<li class="">Introduce new mechanic → Give practice level → Increase difficulty</li>
<li class="">Never introduce 2 new things at once</li>
</ul>
<p>I learned this from watching playtests. When I had "oven + combos" in the same level, people got overwhelmed and quit.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="mid-game-pacing">Mid-Game Pacing<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#mid-game-pacing" class="hash-link" aria-label="Direct link to Mid-Game Pacing" title="Direct link to Mid-Game Pacing" translate="no">​</a></h2>
<p>Levels 3-4 are the "comfort zone." Players understand the basics and are starting to feel competent. This is where the game needs to feel satisfying, not stressful.</p>
<p>I added what I call "flow moments":</p>
<ul>
<li class="">When you serve 3 customers in a row without mistakes</li>
<li class="">When you trigger a combo</li>
<li class="">When you finish a level with time to spare</li>
</ul>
<p>These moments needed to feel GOOD, so I added:</p>
<ul>
<li class="">Sparkle particle effects</li>
<li class="">Encouraging voice lines from customers</li>
<li class="">Score multiplier visual feedback</li>
<li class="">Upbeat music variations</li>
</ul>
<p>The code for "flow state" detection:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">int</span><span class="token plain"> consecutiveSuccessfulServes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ServeCustomer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name keyword" style="color:#00009f">bool</span><span class="token plain"> wasCorrect</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">wasCorrect</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        consecutiveSuccessfulServes</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">consecutiveSuccessfulServes </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">EnterFlowState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        consecutiveSuccessfulServes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">ExitFlowState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">EnterFlowState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Visual feedback</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    backgroundParticles</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">SetActive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Audio feedback</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    music</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pitch </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.1f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Slightly faster, more energetic</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Gameplay bonus</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    scoreMultiplier </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-challenge-ramp">The Challenge Ramp<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#the-challenge-ramp" class="hash-link" aria-label="Direct link to The Challenge Ramp" title="Direct link to The Challenge Ramp" translate="no">​</a></h2>
<p>Levels 5-6 are where I want players to feel challenged but not frustrated.</p>
<p>The key difference:</p>
<ul>
<li class=""><strong>Challenge</strong>: "This is hard, but I can do it if I play better"</li>
<li class=""><strong>Frustration</strong>: "This is impossible and I don't know why I'm failing"</li>
</ul>
<p>How I design challenge:</p>
<ol>
<li class="">
<p><strong>Clear failure reasons</strong><br>
<!-- -->When you lose, you know why (ran out of time, wrong order, missed ingredient)</p>
</li>
<li class="">
<p><strong>Observable improvement</strong><br>
<!-- -->You can see yourself getting faster each attempt</p>
</li>
<li class="">
<p><strong>Player control</strong><br>
<!-- -->Losses feel like YOUR mistakes, not random bad luck</p>
</li>
<li class="">
<p><strong>Achievable goals</strong><br>
<!-- -->Finishing the level is hard but not impossible</p>
</li>
</ol>
<p>I playtested Level 6 about 15 times with different people. The goal was: 70% of players should beat it within 3 attempts.</p>
<p>Too easy → Boring<br>
<!-- -->Too hard → Frustrating<br>
<!-- -->Just right → 2-3 attempts, then satisfaction when you win</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-ending">The Ending<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#the-ending" class="hash-link" aria-label="Direct link to The Ending" title="Direct link to The Ending" translate="no">​</a></h2>
<p>This might sound small, but the ending matters.</p>
<p>When you beat Level 6, Whisker 2 shows:</p>
<ol>
<li class="">Victory screen with final score</li>
<li class="">All the customers you served (little character icons)</li>
<li class="">"Thank you for playing!" message</li>
<li class="">Credits with wholesome music</li>
</ol>
<p>I wanted players to feel accomplished, not just "oh it's over."</p>
<p>I added a little detail: The final customer gives you a thank-you note that says "You're the best chef ever!" with a hand-drawn heart.</p>
<p>One playtester said this made her smile. That's exactly what I wanted.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="designing-for-return-players">Designing for Return Players<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#designing-for-return-players" class="hash-link" aria-label="Direct link to Designing for Return Players" title="Direct link to Designing for Return Players" translate="no">​</a></h2>
<p>Some people play Whisker 2 multiple times. For them, I added:</p>
<p><strong>Score attack mode</strong> (unlocked after first completion)</p>
<ul>
<li class="">Same levels, but trying to beat your high score</li>
<li class="">Leaderboard (local only, no online)</li>
<li class="">New challenge modifiers (2x customers, half time, etc.)</li>
</ul>
<p><strong>Accessibility options</strong> (available from start)</p>
<ul>
<li class="">Adjust customer patience timers</li>
<li class="">Toggle combo system on/off</li>
<li class="">Adjust movement speed</li>
<li class="">Colorblind mode for ingredients</li>
</ul>
<p>These don't affect the core game, but they let different players enjoy it their way.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-learned-about-player-experience">What I Learned About Player Experience<a href="https://liuyisi.com/game-dev-notes/20-player-experience-design-PLACEHOLDER#what-i-learned-about-player-experience" class="hash-link" aria-label="Direct link to What I Learned About Player Experience" title="Direct link to What I Learned About Player Experience" translate="no">​</a></h2>
<p><strong>Every moment teaches players something.</strong></p>
<ul>
<li class="">First 10 seconds → Is this game for me?</li>
<li class="">First minute → How do I play?</li>
<li class="">First level → Am I good at this?</li>
<li class="">Mid-game → Am I improving?</li>
<li class="">Late-game → Can I master this?</li>
<li class="">Ending → Was this worth my time?</li>
</ul>
<p>Designing these moments intentionally makes the difference between a game that's just "okay" and a game that people actually remember.</p>
<p>The technical implementation matters. The mechanics matter. But the player's emotional journey through the game matters most.</p>
<p>That's what I think about now when I design: Not just "what can the player do," but "how should the player FEEL?"</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="design" term="design"/>
        <category label="player-experience" term="player-experience"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[How Programming Constraints Changed the Way I Designed Whisker 2]]></title>
        <id>https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER</id>
        <link href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[I used to think programming was just about implementing designs. Then I learned that programming constraints actually make me a better designer.]]></summary>
        <content type="html"><![CDATA[<p>I used to think programming was just about implementing designs. Then I learned that programming constraints actually make me a better designer.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-original-design">The Original Design<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#the-original-design" class="hash-link" aria-label="Direct link to The Original Design" title="Direct link to The Original Design" translate="no">​</a></h2>
<p>For Whisker 2, I wanted a cooking system where you could combine any ingredients in any order to make custom dishes. Players would experiment and discover recipes.</p>
<p>The design doc in my head:</p>
<ul>
<li class="">20+ ingredients</li>
<li class="">Infinite possible combinations</li>
<li class="">Players create their own recipes</li>
<li class="">Customers request specific dishes</li>
</ul>
<p>This sounded amazing and creative.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-programming-reality">The Programming Reality<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#the-programming-reality" class="hash-link" aria-label="Direct link to The Programming Reality" title="Direct link to The Programming Reality" translate="no">​</a></h2>
<p>Then I tried to implement it.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// My first attempt</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CookingSystem</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">MonoBehaviour</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> availableIngredients</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name">Dish</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CreateDish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> ingredients</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// How do I check if this is a valid dish?</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// How do I know what the customer wanted?</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// How do I give feedback if it's wrong?</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// ...this is going to be way more complicated than I thought</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The problems became obvious fast:</p>
<p><strong>Problem 1: How do I validate "correct" dishes?</strong></p>
<p>If players can make anything, how does the game know what's a sandwich vs what's garbage? Do I hardcode every possible combination?</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ingredients</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Contains</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bread</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> ingredients</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Contains</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cheese</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Sandwich"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ingredients</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Contains</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flour</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> ingredients</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Contains</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sugar</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Cake"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ...repeat for 100+ combinations?</span><br></div></code></pre></div></div>
<p>That would be nightmare to maintain.</p>
<p><strong>Problem 2: How do customers request custom dishes?</strong></p>
<p>If dishes are freeform, I can't show a picture of what they want. I can't say "I want a sandwich with cheese and lettuce" because that assumes sandwiches exist as a concept.</p>
<p><strong>Problem 3: How do players learn recipes?</strong></p>
<p>With no recipe book, players would just throw random ingredients together hoping something works. That's not fun—that's frustrating.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-constraint-driven-redesign">The Constraint-Driven Redesign<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#the-constraint-driven-redesign" class="hash-link" aria-label="Direct link to The Constraint-Driven Redesign" title="Direct link to The Constraint-Driven Redesign" translate="no">​</a></h2>
<p>I stepped back and asked: <strong>What's the smallest system that still feels creative?</strong></p>
<p>The answer: <strong>Predefined recipes, but with customization options.</strong></p>
<p>New design:</p>
<ul>
<li class="">6 base recipes (sandwich, cake, soup, salad, pizza, pasta)</li>
<li class="">Each recipe has 2-3 required ingredients</li>
<li class="">Each recipe has 2-3 optional ingredients for customization</li>
<li class="">Customers request a base dish, sometimes with specific extras</li>
</ul>
<p>This was way simpler to program:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">System</span><span class="token attribute class-name punctuation" style="color:#393A34">.</span><span class="token attribute class-name">Serializable</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Recipe</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token plain"> dishName</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> requiredIngredients</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> optionalIngredients</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name">Dish</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CreateDish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> playerIngredients</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">foreach</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Recipe</span><span class="token plain"> recipe </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> availableRecipes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">HasAllRequired</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playerIngredients</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> recipe</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requiredIngredients</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token class-name">Dish</span><span class="token plain"> dish </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Dish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">recipe</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dishName</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            dish</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AddExtras</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">GetOptionalIngredients</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playerIngredients</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> recipe</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">optionalIngredients</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> dish</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Invalid dish</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">bool</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">HasAllRequired</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> playerIngredients</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Ingredient</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> required</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">foreach</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Ingredient</span><span class="token plain"> req </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> required</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">playerIngredients</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Contains</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-constrained-version-is-actually-better">Why the Constrained Version is Actually Better<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#why-the-constrained-version-is-actually-better" class="hash-link" aria-label="Direct link to Why the Constrained Version is Actually Better" title="Direct link to Why the Constrained Version is Actually Better" translate="no">​</a></h2>
<p>At first, I was disappointed. The original infinite-possibility system felt more "creative" and "ambitious."</p>
<p>But the constrained version had huge advantages:</p>
<p><strong>1. Players could learn the system</strong></p>
<p>With 6 base recipes, players learned them fast. After 2-3 levels, they didn't need to think about "what makes a sandwich." They just grabbed bread, cheese, and lettuce automatically.</p>
<p><strong>2. Customization still felt meaningful</strong></p>
<p>"Make a sandwich" → You choose what goes in it<br>
<!-- -->"Make a sandwich with tomatoes" → You have to remember to add tomatoes</p>
<p>This gave players agency without overwhelming them.</p>
<p><strong>3. The code was maintainable</strong></p>
<p>Adding a new recipe meant creating one Recipe object. Not rewriting the entire validation system.</p>
<p><strong>4. I could add tutorial hints</strong></p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">wantsExtraIngredient </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">dish</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">HasExtra</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">wantedExtra</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">ShowHint</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"The customer wanted extra "</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> customer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">wantedExtra</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"!"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>With freeform dishes, I couldn't give feedback like this.</p>
<p><strong>5. The game felt more focused</strong></p>
<p>Instead of "figure out what's possible," the game became "master these recipes and serve them fast." That's a clearer, more satisfying challenge.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-this-taught-me-about-design">What This Taught Me About Design<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#what-this-taught-me-about-design" class="hash-link" aria-label="Direct link to What This Taught Me About Design" title="Direct link to What This Taught Me About Design" translate="no">​</a></h2>
<p><strong>Programming constraints aren't limitations—they're design tools.</strong></p>
<p>When I hit a programming wall, it's usually because my design is too vague or too complicated.</p>
<p>The constraint forced me to ask:</p>
<ul>
<li class="">What's the core experience I want?</li>
<li class="">What's the simplest version that delivers that experience?</li>
<li class="">What can I cut without losing what matters?</li>
</ul>
<p>Now when I design a system, I prototype it in code early. Not to "lock in" the design, but to find out if it actually works.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-process-i-use-now">The Process I Use Now<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#the-process-i-use-now" class="hash-link" aria-label="Direct link to The Process I Use Now" title="Direct link to The Process I Use Now" translate="no">​</a></h2>
<ol>
<li class="">
<p><strong>Design the ideal version</strong><br>
<!-- -->(What would this look like with infinite time and perfect code?)</p>
</li>
<li class="">
<p><strong>Write pseudocode or a rough implementation</strong><br>
<!-- -->(What are the actual technical challenges?)</p>
</li>
<li class="">
<p><strong>Identify the constraints</strong><br>
<!-- -->(What's hard? What's repetitive? What's fragile?)</p>
</li>
<li class="">
<p><strong>Redesign around the constraints</strong><br>
<!-- -->(What's a simpler version that solves the same problem?)</p>
</li>
<li class="">
<p><strong>Test if the simpler version still feels good</strong><br>
<!-- -->(Did I lose something important?)</p>
</li>
</ol>
<p>Usually, the simpler version is better anyway.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="examples-from-other-systems">Examples from Other Systems<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#examples-from-other-systems" class="hash-link" aria-label="Direct link to Examples from Other Systems" title="Direct link to Examples from Other Systems" translate="no">​</a></h2>
<p><strong>Oven Timer System</strong></p>
<p>Original idea: Players manually control temperature and time<br>
<!-- -->Programming constraint: That's a lot of UI and state management<br>
<!-- -->Better design: Click to start cooking, done automatically after fixed time<br>
<!-- -->Result: Still satisfying, way easier to implement</p>
<p><strong>Customer Patience System</strong></p>
<p>Original idea: Patience decreases based on real-time clock<br>
<!-- -->Programming constraint: What happens if player pauses? What if frame rate drops?<br>
<!-- -->Better design: Patience decreases based on game ticks, not wall time<br>
<!-- -->Result: More predictable, works the same on any device</p>
<p><strong>Ingredient Placement</strong></p>
<p>Original idea: Drag and drop ingredients anywhere on counters<br>
<!-- -->Programming constraint: Collision detection and state management nightmare<br>
<!-- -->Better design: Click ingredient → Click counter → Automatically placed in grid<br>
<!-- -->Result: Faster to play, easier to code</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-lesson">The Lesson<a href="https://liuyisi.com/game-dev-notes/19-programming-constraints-PLACEHOLDER#the-lesson" class="hash-link" aria-label="Direct link to The Lesson" title="Direct link to The Lesson" translate="no">​</a></h2>
<p><strong>Good code makes good games. Good games make good code.</strong></p>
<p>I used to see programming as the "technical part" after design. Now I see them as the same thing.</p>
<p>The best designs are the ones that are simple to implement, easy to maintain, and clear to players. Programming constraints help me find those designs.</p>
<p>When something is hard to code, it's often a sign that the design needs another pass.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="programming" term="programming"/>
        <category label="design" term="design"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Feature I Was Most Excited About Had to Go]]></title>
        <id>https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER</id>
        <link href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[I spent two weeks building a weather system for Whisker 2. It was going to make the game feel alive and dynamic. Then I realized it was making the game worse, not better.]]></summary>
        <content type="html"><![CDATA[<p>I spent two weeks building a weather system for Whisker 2. It was going to make the game feel alive and dynamic. Then I realized it was making the game worse, not better.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-idea">The Idea<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#the-idea" class="hash-link" aria-label="Direct link to The Idea" title="Direct link to The Idea" translate="no">​</a></h2>
<p>I wanted Whisker 2 to have a day/night cycle with weather that affected gameplay. The idea was:</p>
<ul>
<li class=""><strong>Sunny days</strong>: Normal gameplay</li>
<li class=""><strong>Rainy days</strong>: Customers arrive slower but stay longer</li>
<li class=""><strong>Nighttime</strong>: Fewer customers but higher tips</li>
</ul>
<p>I thought this would add variety and strategy. Players would adapt their approach based on the weather.</p>
<p>I even made a mood board with cozy rainy cafe aesthetics. I was so excited about this feature.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-implementation">The Implementation<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#the-implementation" class="hash-link" aria-label="Direct link to The Implementation" title="Direct link to The Implementation" translate="no">​</a></h2>
<p>I built the entire system:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">WeatherSystem</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">MonoBehaviour</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">enum</span><span class="token plain"> </span><span class="token class-name">WeatherType</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Sunny</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Rainy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Cloudy </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token class-name">WeatherType</span><span class="token plain"> currentWeather</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">float</span><span class="token plain"> timeOfDay </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 0 = morning, 1 = night</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        timeOfDay </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> Time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">deltaTime </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">timeOfDay </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> timeOfDay </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">UpdateLighting</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">UpdateWeatherEffects</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">UpdateLighting</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name keyword" style="color:#00009f">float</span><span class="token plain"> lightIntensity </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Mathf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Lerp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.3f</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1f</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeOfDay</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        RenderSettings</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ambientIntensity </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> lightIntensity</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">UpdateWeatherEffects</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentWeather </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> WeatherType</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Rainy</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            customerSpawnRate </span><span class="token operator" style="color:#393A34">*=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.7f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            customerPatience </span><span class="token operator" style="color:#393A34">*=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.3f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>I added:</p>
<ul>
<li class="">Particle systems for rain</li>
<li class="">Dynamic lighting that changed over time</li>
<li class="">Different background music for each weather type</li>
<li class="">UI indicators showing current weather</li>
<li class="">Modified customer AI for weather-based behavior</li>
</ul>
<p>It took me two full weeks of work.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem">The Problem<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>When I finally tested it in a full gameplay session, I noticed something horrible:</p>
<p><strong>I had no idea why I was failing.</strong></p>
<p>When I lost, I didn't know if it was because:</p>
<ul>
<li class="">I played badly</li>
<li class="">The weather made it harder</li>
<li class="">The time of day changed difficulty</li>
<li class="">My strategy was wrong</li>
</ul>
<p>The weather system added randomness, but not the good kind. It just made the game feel unpredictable and confusing.</p>
<p>Even worse: The lighting changes made it harder to see ingredients. At night, I kept grabbing the wrong items because everything looked darker.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-i-realized-it-had-to-go">When I Realized It Had to Go<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#when-i-realized-it-had-to-go" class="hash-link" aria-label="Direct link to When I Realized It Had to Go" title="Direct link to When I Realized It Had to Go" translate="no">​</a></h2>
<p>I showed the game to my friend who had played the earlier version. She said:</p>
<p>"This is cool but... I liked it better before. It feels more stressful now and I can't tell why."</p>
<p>That's when it hit me: <strong>The weather system wasn't making the game more fun. It was making it more complicated.</strong></p>
<p>The core loop of Whisker 2 is:</p>
<ol>
<li class="">See customer order</li>
<li class="">Grab ingredients</li>
<li class="">Cook</li>
<li class="">Serve</li>
<li class="">Feel good when customer is happy</li>
</ol>
<p>The weather system disrupted that loop without adding anything meaningful to it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-i-think-i-made-this-mistake">Why I Think I Made This Mistake<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#why-i-think-i-made-this-mistake" class="hash-link" aria-label="Direct link to Why I Think I Made This Mistake" title="Direct link to Why I Think I Made This Mistake" translate="no">​</a></h2>
<p>Looking back, I think I added weather because:</p>
<ol>
<li class="">
<p><strong>It sounded impressive.</strong> "Dynamic weather system" sounds way cooler than "you serve food to customers."</p>
</li>
<li class="">
<p><strong>I saw it in other games.</strong> Stardew Valley has seasons. Animal Crossing has weather. Those games feel cozy, so I thought weather would make my game feel cozy too.</p>
</li>
<li class="">
<p><strong>I wanted to show off technically.</strong> Implementing a weather system felt like "real game development" compared to just making a simple cafe game.</p>
</li>
</ol>
<p>But I forgot the most important question: <strong>Does this make my specific game better?</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-decision">The Decision<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#the-decision" class="hash-link" aria-label="Direct link to The Decision" title="Direct link to The Decision" translate="no">​</a></h2>
<p>I spent two weeks building it. Deleting it felt horrible.</p>
<p>But I did it.</p>
<p>I commented out all the weather code and played the game again. Immediately, it felt better. Clearer. More focused. The core loop worked again.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-learned">What I Learned<a href="https://liuyisi.com/game-dev-notes/18-feature-that-had-to-go-PLACEHOLDER#what-i-learned" class="hash-link" aria-label="Direct link to What I Learned" title="Direct link to What I Learned" translate="no">​</a></h2>
<p><strong>Not every cool idea belongs in every game.</strong></p>
<p>Weather works in Stardew Valley because the game is about planning around seasons. Weather works in Animal Crossing because the game is about living in a world that changes.</p>
<p>But Whisker 2 is about fast-paced cafe management and making customers happy. Weather just got in the way.</p>
<p>The lesson isn't "weather systems are bad." It's: <strong>Every feature needs to serve the core experience, or it's a distraction.</strong></p>
<p>Now when I want to add a feature, I ask:</p>
<ol>
<li class="">Does this make the core loop better?</li>
<li class="">Or does it just make the game more complicated?</li>
<li class="">Am I adding this because it serves the game, or because it sounds cool?</li>
<li class="">If I remove it, does the game get worse?</li>
</ol>
<p>Sometimes the hardest thing as a developer is recognizing when your favorite idea doesn't actually fit the game you're making.</p>
<p>I still have the weather code backed up. Maybe someday I'll make a game where it belongs. But for Whisker 2, letting it go was the right choice.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="design" term="design"/>
        <category label="scope" term="scope"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Thought Players Would Love This Mechanic. They Didn't. Here's What I Changed.]]></title>
        <id>https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER</id>
        <link href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[One of the mechanics I was most excited about in Whisker 2 completely confused players during playtesting. Here's what I learned about the difference between what designers think and what players actually experience.]]></summary>
        <content type="html"><![CDATA[<p>One of the mechanics I was most excited about in Whisker 2 completely confused players during playtesting. Here's what I learned about the difference between what designers think and what players actually experience.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-designed">What I Designed<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#what-i-designed" class="hash-link" aria-label="Direct link to What I Designed" title="Direct link to What I Designed" translate="no">​</a></h2>
<p>I added a combo system where if you served ingredients in a specific order, you'd get bonus points. I thought this would add depth to the gameplay and reward skilled players.</p>
<p>The system worked like this:</p>
<ol>
<li class="">Pick up bread → Pick up cheese → Pick up lettuce = Sandwich Combo! (+50 points)</li>
<li class="">Pick up flour → Pick up sugar → Pick up eggs = Cake Combo! (+100 points)</li>
</ol>
<p>I implemented it with a simple queue system:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token class-name">Queue</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> ingredientQueue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Queue</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&lt;</span><span class="token constructor-invocation class-name keyword" style="color:#00009f">string</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PickupIngredient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token plain"> ingredientName</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ingredientQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Enqueue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ingredientName</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ingredientQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Count </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ingredientQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Dequeue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Only track last 3 ingredients</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CheckForCombo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CheckForCombo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token class-name punctuation" style="color:#393A34">[</span><span class="token class-name punctuation" style="color:#393A34">]</span><span class="token plain"> currentCombo </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ingredientQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ToArray</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Check if it matches any combo patterns</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">IsCombo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentCombo</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sandwichCombo</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        score </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">ShowComboText</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Sandwich Combo!"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-expected">What I Expected<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#what-i-expected" class="hash-link" aria-label="Direct link to What I Expected" title="Direct link to What I Expected" translate="no">​</a></h2>
<p>I thought players would:</p>
<ol>
<li class="">Notice the combo text appearing</li>
<li class="">Experiment with different ingredient orders</li>
<li class="">Learn the patterns</li>
<li class="">Optimize their ingredient pickup routes</li>
</ol>
<p>I was imagining players speedrunning to get perfect combos.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-players-actually-did">What Players Actually Did<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#what-players-actually-did" class="hash-link" aria-label="Direct link to What Players Actually Did" title="Direct link to What Players Actually Did" translate="no">​</a></h2>
<p>During the first playtest, literally nobody mentioned combos. Zero people.</p>
<p>I asked: "Did you notice the combo system?"</p>
<p>Most people said: "What combo system?"</p>
<p>A few people said: "Oh, I saw some text flash but I was focused on the orders."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-i-think-that-happened">Why I Think That Happened<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#why-i-think-that-happened" class="hash-link" aria-label="Direct link to Why I Think That Happened" title="Direct link to Why I Think That Happened" translate="no">​</a></h2>
<p>After watching people play, I realized three problems:</p>
<p><strong>Problem 1: The feedback was too subtle</strong>
The combo text appeared for 1 second at the top of the screen. But players were looking at:</p>
<ul>
<li class="">The customer orders (center-right)</li>
<li class="">The ingredients they were picking up (bottom)</li>
<li class="">The oven timer (top-right)</li>
</ul>
<p>Nobody was looking at the top-center where combo text appeared.</p>
<p><strong>Problem 2: The reward wasn't obvious</strong>
+50 points sounds good to me as the designer. But players had no idea if that was a lot or a little. The score was just a number that went up.</p>
<p><strong>Problem 3: Players were already overwhelmed</strong>
During early levels, players were still learning:</p>
<ul>
<li class="">Where ingredients are</li>
<li class="">How to use the oven</li>
<li class="">How to serve customers</li>
<li class="">How to manage time</li>
</ul>
<p>Adding "also learn combo patterns" on top of that was too much.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-changed">What I Changed<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#what-i-changed" class="hash-link" aria-label="Direct link to What I Changed" title="Direct link to What I Changed" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="change-1-made-feedback-impossible-to-miss">Change 1: Made feedback impossible to miss<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#change-1-made-feedback-impossible-to-miss" class="hash-link" aria-label="Direct link to Change 1: Made feedback impossible to miss" title="Direct link to Change 1: Made feedback impossible to miss" translate="no">​</a></h3>
<p>I moved the combo text to follow the player character and made it way bigger:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ShowComboText</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token plain"> comboName</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">GameObject</span><span class="token plain"> comboPopup </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Instantiate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">comboTextPrefab</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> playerTransform</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">position </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> Vector3</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">up</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Quaternion</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">identity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    comboPopup</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">transform</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">SetParent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playerTransform</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Follow player</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">TextMeshPro</span><span class="token plain"> textComponent </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> comboPopup</span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">GetComponent</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">TextMeshPro</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    textComponent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> comboName </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">" +50 Points!"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    textComponent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fontSize </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Much bigger</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Animate it floating up</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">StartCoroutine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">FloatAndFade</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">comboPopup</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="change-2-made-the-reward-tangible">Change 2: Made the reward tangible<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#change-2-made-the-reward-tangible" class="hash-link" aria-label="Direct link to Change 2: Made the reward tangible" title="Direct link to Change 2: Made the reward tangible" translate="no">​</a></h3>
<p>Instead of just points, combos now:</p>
<ul>
<li class="">Gave you +5 seconds on the customer timer (something players actually cared about)</li>
<li class="">Made a satisfying "ding!" sound</li>
<li class="">Showed sparkle particle effects</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="change-3-delayed-the-introduction">Change 3: Delayed the introduction<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#change-3-delayed-the-introduction" class="hash-link" aria-label="Direct link to Change 3: Delayed the introduction" title="Direct link to Change 3: Delayed the introduction" translate="no">​</a></h3>
<p>I removed combos from the first 3 levels entirely. Players learned the core loop first. Then level 4 showed a tutorial message: "Try picking up ingredients in order for bonus time!"</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="change-4-simplified-the-combos">Change 4: Simplified the combos<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#change-4-simplified-the-combos" class="hash-link" aria-label="Direct link to Change 4: Simplified the combos" title="Direct link to Change 4: Simplified the combos" translate="no">​</a></h3>
<p>Instead of 8 different combo patterns, I reduced it to 3 common ones that matched the most frequent orders. This made them easier to learn and players naturally did them while fulfilling orders anyway.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-result">The Result<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#the-result" class="hash-link" aria-label="Direct link to The Result" title="Direct link to The Result" translate="no">​</a></h2>
<p>In the second playtest:</p>
<ul>
<li class="">80% of players mentioned combos without me asking</li>
<li class="">Several players actively tried to trigger them</li>
<li class="">One player said "Oh that's why the sparkles happened!"</li>
<li class="">Average scores went up because people got more time bonuses</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-learned">What I Learned<a href="https://liuyisi.com/game-dev-notes/17-player-feedback-iteration-PLACEHOLDER#what-i-learned" class="hash-link" aria-label="Direct link to What I Learned" title="Direct link to What I Learned" translate="no">​</a></h2>
<p><strong>Design hypothesis ≠ player experience.</strong></p>
<p>Just because I understand a mechanic doesn't mean players will notice it, care about it, or figure it out.</p>
<p>The most important lesson: <strong>Watch people play. Don't just listen to what they say—watch what they actually do.</strong></p>
<p>Players told me the game was "fun" but they didn't engage with combos at all. If I had only done surveys instead of observation, I would have thought the combo system worked fine.</p>
<p>Now before I add any mechanic, I ask:</p>
<ol>
<li class="">Will players even notice this exists?</li>
<li class="">Is the feedback clear and immediate?</li>
<li class="">Is this teaching them too many things at once?</li>
<li class="">What's the smallest version of this I can test?</li>
</ol>
<p>Testing your assumptions is the difference between "I think this is good" and "I know this works."</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="playtesting" term="playtesting"/>
        <category label="design" term="design"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[What Makes a Game Feel Good? What I Learned Making Whisker 2]]></title>
        <id>https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER</id>
        <link href="https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[After making several games, I started noticing patterns in what made games feel satisfying to play versus games that felt frustrating or boring.]]></summary>
        <content type="html"><![CDATA[<p>After making several games, I started noticing patterns in what made games feel satisfying to play versus games that felt frustrating or boring.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="movement-feel">Movement Feel<a href="https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER#movement-feel" class="hash-link" aria-label="Direct link to Movement Feel" title="Direct link to Movement Feel" translate="no">​</a></h2>
<p>One of the biggest things I learned was how much small details matter for movement. In Whisker 2, I spent a lot of time tweaking how the player moved around the kitchen.</p>
<p>At first, I just used Unity's default character controller settings. The movement worked, but it felt sluggish. When I tried to pick up ingredients quickly, the character would take too long to start moving or stop.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Original movement - felt too slow</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">float</span><span class="token plain"> horizontal </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetAxis</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Horizontal"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">float</span><span class="token plain"> vertical </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetAxis</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Vertical"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">Vector3</span><span class="token plain"> movement </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Vector3</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">horizontal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vertical</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    transform</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">movement </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> moveSpeed </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> Time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">deltaTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>I ended up adding acceleration and deceleration instead of instant movement. This made it feel more responsive.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Improved movement with acceleration</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">float</span><span class="token plain"> horizontal </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetAxis</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Horizontal"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">float</span><span class="token plain"> vertical </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetAxis</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Vertical"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">Vector3</span><span class="token plain"> targetVelocity </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Vector3</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">horizontal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vertical</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> moveSpeed</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    currentVelocity </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Vector3</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Lerp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentVelocity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> targetVelocity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> acceleration </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> Time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">deltaTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    transform</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentVelocity </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> Time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">deltaTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The difference seems small in code, but in gameplay it made the character feel way more responsive.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="feedback-systems">Feedback Systems<a href="https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER#feedback-systems" class="hash-link" aria-label="Direct link to Feedback Systems" title="Direct link to Feedback Systems" translate="no">​</a></h2>
<p>Another thing I learned was that players need constant feedback. Every action should have a visual or audio response.</p>
<p>In Whisker, when you picked up an ingredient, the original version just made the ingredient disappear and appear in your hand. But during playtesting, people said they weren't sure if they actually grabbed it.</p>
<p>I added:</p>
<ul>
<li class="">A small "pop" sound effect when picking up ingredients</li>
<li class="">The ingredient briefly scales up before moving to your hand</li>
<li class="">A subtle particle effect</li>
</ul>
<p>These tiny details made players feel way more confident about what they were doing.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pacing-and-difficulty">Pacing and Difficulty<a href="https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER#pacing-and-difficulty" class="hash-link" aria-label="Direct link to Pacing and Difficulty" title="Direct link to Pacing and Difficulty" translate="no">​</a></h2>
<p>The hardest thing to balance was difficulty. I wanted Whisker 2 to be challenging but not frustrating.</p>
<p>Originally, customer patience timers were the same throughout the whole game. But that meant either early levels were too easy or later levels were impossible.</p>
<p>I ended up using a curve for patience:</p>
<ul>
<li class="">Early customers: 60 seconds to complete order</li>
<li class="">Mid-game customers: 45 seconds</li>
<li class="">Late-game customers: 30 seconds</li>
</ul>
<p>But I also added ways to earn back time, like perfect ingredient combos giving you +5 seconds.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-cozy-games-are-different">Why Cozy Games Are Different<a href="https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER#why-cozy-games-are-different" class="hash-link" aria-label="Direct link to Why Cozy Games Are Different" title="Direct link to Why Cozy Games Are Different" translate="no">​</a></h2>
<p>Working on Whisker taught me that cozy games have different feedback rules than action games. In Sharpshooter, missing a shot felt punishing. But in Whisker, I wanted mistakes to feel like opportunities to learn, not failures.</p>
<p>That meant:</p>
<ul>
<li class="">No instant game-overs</li>
<li class="">Customers get disappointed but don't rage-quit</li>
<li class="">You can recover from mistakes</li>
<li class="">Visual feedback stays positive and encouraging</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-i-think-about-now">What I Think About Now<a href="https://liuyisi.com/game-dev-notes/16-game-feel-philosophy-PLACEHOLDER#what-i-think-about-now" class="hash-link" aria-label="Direct link to What I Think About Now" title="Direct link to What I Think About Now" translate="no">​</a></h2>
<p>When I start a new game now, I ask:</p>
<ul>
<li class="">How should movement feel? Fast? Heavy? Floaty?</li>
<li class="">What's the main action the player does? How can I make that feel satisfying?</li>
<li class="">What feedback does the player need to feel confident?</li>
<li class="">What's the pace? Frantic? Relaxed? Tense?</li>
<li class="">How do I want failures to feel?</li>
</ul>
<p>These questions help me design mechanics that actually feel good to play, not just technically correct.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="design" term="design"/>
        <category label="game-feel" term="game-feel"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Whisker 2 Devlog: Bakery: the level that was remade 3+ times. Here's why]]></title>
        <id>https://liuyisi.com/game-dev-notes/15-whisker2-bakery-remade</id>
        <link href="https://liuyisi.com/game-dev-notes/15-whisker2-bakery-remade"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[The Bakery level is the singular level I have probably spent the most amount of time on.]]></summary>
        <content type="html"><![CDATA[<p>The Bakery level is the singular level I have probably spent the most amount of time on.</p>
<p>The first version of the Bakery would be the centerpiece of the original Whisker game that was created during COSMOS. I would later renovate it to work with the new features I had for Whisker 2. It would be reworked the third time when I added in my two new levels.</p>
<p>The progression of the layout for the Bakery level would become increasingly complex over time in order to accommodate all the new mechanics, at first what we wanted to add and now what I want to add. The first Bakery was made only to feature the mechanics for cooking, talking with customers, and delivering the pastries. The second version would expand on this by adding in the feature to open and close appliances, and in a refrigerator. The layout for the walls were reworked to make it more realistic by scaling down them and adding in little sprites that fleshed out the environment.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/4B2xm5SCnhU?rel=0&amp;modestbranding=1" title="Whisker Bakery level (version 1)" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/cuHi2Udsnes?rel=0&amp;modestbranding=1" title="Whisker 2 Bakery level (version 2)" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>The third version of the Bakery was made with the idea of making it more cohesive with the other levels. Initially, the other levels were made with the idea of expanding the scope of the game and it would do that well; But, it became apparent that the scope was not out of reach of the second version of Bakery. This time the Bakery was reformed to have its own unique feature and the layout was changed to be more similar with the other levels in order to have more cohesiveness. This time the Bakery would be made into two areas: a kitchen and a separate customer section. The expanded Bakery reduced the clutter in version 2 and made the layout more clear.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/H2e9RR1fVUw?rel=0&amp;modestbranding=1" title="Whisker 2 Bakery level (version 3)" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>I am still working on Whisker 2, but one of the funnest aspects was being able to add in these new levels and still being able to work on the old ones.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Whisker 2 Devlog: why my character kept falling into the ground and how I fixed it]]></title>
        <id>https://liuyisi.com/game-dev-notes/14-whisker2-character-falling</id>
        <link href="https://liuyisi.com/game-dev-notes/14-whisker2-character-falling"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[Today, I fixed one of the bugs that had been plaguing my development ever since I switched the character sprite in my game.]]></summary>
        <content type="html"><![CDATA[<p>Today, I fixed one of the bugs that had been plaguing my development ever since I switched the character sprite in my game.</p>
<p>The problem started when I switched the art. The reason why I wanted to switch the art was because I felt like the art didn't fit the style of the game anymore now that it was under my art direction.</p>
<p>However, when I tried to add in the new assets, my character kept on sinking into the ground a little. At first, I thought it was because I had forgotten to adjust the colliders. But, even after adjusting it, it didn't work.</p>
<p>Afterwards, I checked various different things that could have led to my character sinking into the ground: changing the pivot point, looking through the code, increasing the collider.</p>
<p>After a long bit of changing things and seeing it not work, I researched a little into the problem and it turned out that all I had to change was the height in the character controller. One simple problem took me 45 minutes of debugging.</p>
<p>But, I learned that sometimes the best way to fix a bug is to look up the problem. Game development once again proves to me how I have to continue learning and I can't wait to learn more.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/bwuPDp3gQX4?rel=0&amp;modestbranding=1" title="Whisker 2 character falling bug fix" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
        <category label="debugging" term="debugging"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Whisker 2 Devlog: How I Overcame Drawing Side Profiles, Mostly]]></title>
        <id>https://liuyisi.com/game-dev-notes/13-whisker2-side-profiles</id>
        <link href="https://liuyisi.com/game-dev-notes/13-whisker2-side-profiles"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[One of the biggest problems that came-up in my journey to draw my own characters was drawing the side profile.]]></summary>
        <content type="html"><![CDATA[<p>One of the biggest problems that came-up in my journey to draw my own characters was drawing the side profile.</p>
<div style="display:flex;gap:2rem;align-items:flex-start"><div style="flex:0 0 55%;display:flex;flex-wrap:wrap;gap:0.5rem"><img src="https://liuyisi.com/devlog/13-whisker2-side-profiles/p1.png" alt="Side profile iteration 1" style="flex:1 1 30%;min-width:0;border-radius:6px"><img src="https://liuyisi.com/devlog/13-whisker2-side-profiles/p2.png" alt="Side profile iteration 2" style="flex:1 1 30%;min-width:0;border-radius:6px"><img src="https://liuyisi.com/devlog/13-whisker2-side-profiles/p3.png" alt="Side profile iteration 3" style="flex:1 1 30%;min-width:0;border-radius:6px"><img src="https://liuyisi.com/devlog/13-whisker2-side-profiles/p4.png" alt="Side profile iteration 4" style="flex:1 1 30%;min-width:0;border-radius:6px"><img src="https://liuyisi.com/devlog/13-whisker2-side-profiles/p5.png" alt="Side profile iteration 5" style="flex:1 1 30%;min-width:0;border-radius:6px"></div><div style="flex:1"><p>It was hard for me personally because I haven't had a lot of experience drawing different perspectives. I usually drew my characters when they were only facing the front so I lacked a lot of the anatomy knowledge to begin this part of character sprite.</p><p>My first iteration of the side profile for my original unicorn-human character looked like a completely different person. Even when I drew the first and second version of the side profile for the new fully human chef character, they still looked like completely different people.</p><p>The most glaring issue was the size of the character: the difference in size between the front and sides was so astronomical that they could hardly be considered the same person. This was why I decided to redraw my entire character with the idea of allowing them to look consistent in mind.</p><p>I made my third version with the front and side profiles together which meant I could continually check if they looked similar to each other. The end result was much better. They actually looked cohesive.</p><p>Nevertheless, I still need to polish the minor flaws, but I felt like I learned so much just by redrawing my character.</p></div></div>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="art" term="art"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Designing in a Vacuum vs. Designing for Your Game]]></title>
        <id>https://liuyisi.com/game-dev-notes/12-whisker2-character-design</id>
        <link href="https://liuyisi.com/game-dev-notes/12-whisker2-character-design"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[For Whisker 2, I decided to make the bold design to make my own assets for the first time since creating the start screen for Murder Mystery.]]></summary>
        <content type="html"><![CDATA[<p>For Whisker 2, I decided to make the bold design to make my own assets for the first time since creating the start screen for Murder Mystery.</p>
<p>For the past few projects, I have mainly relied on Unity asset store for my models, but watching my teammates make art for Whisker inspired me to create my own assets. Soon, I booted up Krita and started drawing.</p>
<p>I wanted Whisker 2 to have characters that were more humanlike so I decided to make a human chef with unicorn features. I chose a unicorn specifically because they are one of my favorite animals and I thought it would be nice to be able to bring them into my game.</p>
<p>But when I added them into my levels, they stood out and not in a nice way. One of the big problems with the new character was that their dull, pastel color scheme didn't match with the vibrant environment. I received some feedback from my friends who also found that the color palette didn't work, but also they thought that the numerous hair strands were distracting.</p>
<p>I realized that the biggest reason for why this design failed was because I made it in a vacuum. It would have looked nice by itself, but it wasn't cohesive with the actual game it was for.</p>
<p>So I got to work, this time I used a more warm color palette that fit the vibe of Bakery much more. I would make a new design, but this time they were fully human and more vibrant. I made a third version after this mainly to reduce the darkness of the hair, but to also polish the design more. Even so, I really enjoyed being able to create my own assets.</p>
<p>I felt like it really allowed me to understand the more artistic aspects of game development and I hope to be able to make my own 3D models in the future too!</p>
<div class="before-after"><div class="before-after__item"><p class="before-after__label">First Design</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/12-whisker2-character-design/sketch.png" alt="First character design" class="img_ev3q"></div><div class="before-after__item"><p class="before-after__label">Second Version</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/12-whisker2-character-design/final.png" alt="Second version character design" class="img_ev3q"></div><div class="before-after__item"><p class="before-after__label">Final Design</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/12-whisker2-character-design/front.png" alt="Final character design" class="img_ev3q"></div></div>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="design" term="design"/>
        <category label="art" term="art"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Whisker 2 Devlog: The reason why I made three different level layouts for Whisker 2]]></title>
        <id>https://liuyisi.com/game-dev-notes/11-whisker2-three-levels</id>
        <link href="https://liuyisi.com/game-dev-notes/11-whisker2-three-levels"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[When I started making my sequel to Whisker, my biggest upgrade was adding in more unique levels.]]></summary>
        <content type="html"><![CDATA[<p>When I started making my sequel to Whisker, my biggest upgrade was adding in more unique levels.</p>
<p>The original Whisker only had one unique environment due to time constraints, but this time I was free to add in more. The first addition I wanted to add was a gardening level because this was one of the biggest features that was brought up at the initial discussions, but never made it to reality. However, I felt that two levels would have been odd, this is a little ironic, and so I added in one more level based on the beverage mechanic: a cafe.</p>
<p>Although initially these were just ideas from our brainstorm session, I made it into a full-fledged design. These two designs were to be different from the bakery level and have its own mechanic and aesthetic. This was different from the initial ideas during COSMOS as it expanded these mechanics that were only originally meant to be additional features into their own levels.</p>
<p>For the gardening level, I changed it into a farm after thinking about how I could portray the carefree, relaxing nature of being outside and tending to your plants. I expanded the level so that it could encapsulate the feel of freedom.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/_vIqG1tlV7U?rel=0&amp;modestbranding=1" title="Whisker 2 Garden level showcase" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>The cafe level was meant to go in the opposite direction: it was to be more elegant and sophisticated. I wanted this level to have some sort of sleek stylishness so I used more muted colors and designed the level to be more rigid, but also have the level be more organized.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/a1iQlhK-UDs?rel=0&amp;modestbranding=1" title="Whisker 2 Cafe level showcase" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>Although these levels are very different from the initial ideas that formed them, the current version expands the game more.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Four UI Iterations: What I Learned About Feedback]]></title>
        <id>https://liuyisi.com/game-dev-notes/10-ui-journey-summary</id>
        <link href="https://liuyisi.com/game-dev-notes/10-ui-journey-summary"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[One of the hardest things to learn in Unity was how to use the UI system.]]></summary>
        <content type="html"><![CDATA[<p>One of the hardest things to learn in Unity was how to use the UI system.</p>
<p>One of the best parts of Unity is how many different things you can do. To this day, I still find myself learning new things about the engine. One of examples of this is the UI system. It offers so many different kinds of UI components like buttons, sliders, toggle buttons, and others. This made it very daunting at first.</p>
<p>When I made my first original game Murder Mystery game, one of the hardest parts was getting the UI to scale correctly. At the time, I didn't know that there was a specific feature to lockdown the size instead of having it scale based on different screen sizes. My first attempt was overly cluttered as well. Instead of using only a couple of Canvas, I made a new canvas for every single UI tool I used. Nevertheless, it was still very fun to experiment with the UI system. Murder Mystery game's clue mechanic relied solely on toggles and the interrogate feature worked because of buttons. The story-based nature of the game meant I used a lot of buttons which ultimately allowed me to learn so much about the UI system.</p>
<p>One of coolest things, in my opinion, was my confetti win screen. After the player guessed the murderer correctly, they would be displayed a rainbow confetti particle system, a congratulatory message, and two buttons: restart or quit. This game was the first time I added in a start screen and a win screen which were both very cool.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/SYhPLr1Ui94?rel=0&amp;modestbranding=1" title="Murder Mystery UI demonstration" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>My UI journey would be temporarily put on hold while I was working on my Sharpshooter because it didn't really require as much UI as my previous game.</p>
<p>But when I started working on my new infinite runner, I continued to improve my UI skills. This time I carried over the start screen and end screen features of Murder Mystery and expanded it. My new start screen featured the ability for the user to select the gender of the player and the level they wanted to play. I added in a slider that tracked the user's progress in getting enough points to move to the next level and finally figured out how to prevent the UI from shifting in size.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/YHALsPbuskk?rel=0&amp;modestbranding=1" title="Infinite Runner UI demonstration" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>These skills would then be applied to Whisker.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/3W6K_8m55_Q?rel=0&amp;modestbranding=1" title="Whisker UI demonstration" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>But, it was really Whisker 2 that I expanded my UI knowledge again. This time new development was adding in a pause panel and a settings panel.</p>
<p>The experience at COSMOS made me realize that UI wasn't just for visual flair, but to improve user accessibility too!</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/_vIqG1tlV7U?rel=0&amp;modestbranding=1" title="Whisker 2 UI demonstration" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="ui" term="ui"/>
        <category label="devlog" term="devlog"/>
        <category label="reflection" term="reflection"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Five Days Debugging a State Machine Bug]]></title>
        <id>https://liuyisi.com/game-dev-notes/09-whisker2-order-bug</id>
        <link href="https://liuyisi.com/game-dev-notes/09-whisker2-order-bug"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[One of my biggest problems after I added in a lot of new features expanding the core gameplay of Whisker 2 was the player character becoming unable to finish orders.]]></summary>
        <content type="html"><![CDATA[<p>One of my biggest problems after I added in a lot of new features expanding the core gameplay of Whisker 2 was the player character becoming unable to finish orders.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/JX4y69gnt_g?rel=0&amp;modestbranding=1" title="Whisker 2 order bug" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>This was a big issue that I wanted to fix as soon as possible because it prevented the game from flowing at all and allowed me to check up on potential other errors because I couldn't interact with the customers.</p>
<p>I originally thought that the problem had to do with the code so I spent a significant amount of time combing through it to try to find the bug. I did change some of the code such as reducing the mouse click to only function as the control for giving the customers food instead of also allowing for food to be dropped(I had a new key for that now, Q.)</p>
<p>It took further investigation and shifting my focus from the code to the actual implementation itself to realize that the true cause of my problems was I had accidentally linked the script to both the parent object and the child object specifically meant to handle this. The problem came primarily because of me linking it to the parent object as it disabled the colliders.</p>
<p>Nevertheless, I was able to fix it and learned the important lesson that bugs don't always come from code when it comes to game development.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
        <category label="debugging" term="debugging"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Whisker 2 Devlog: The inspiration for the UI for Whisker and what is next for Whisker 2]]></title>
        <id>https://liuyisi.com/game-dev-notes/08-whisker2-ui-inspiration</id>
        <link href="https://liuyisi.com/game-dev-notes/08-whisker2-ui-inspiration"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[After the COSMOS, one of the biggest things I wanted to work on for Whisker 2 was improving the UI.]]></summary>
        <content type="html"><![CDATA[<p>After the COSMOS, one of the biggest things I wanted to work on for Whisker 2 was improving the UI.</p>
<p>The UI for Whisker was very limited. For starters, it didn't have a level menu that allowed the player to go back to the main menu which became very apparent in the last COSMOS-wide playtest as I would have to restart the game every time someone new came to play it.</p>
<p>Consequently, my first addition for my UI improvement plan was just adding in a pause button. From there, I added in a pause menu that allowed for the player to adjust the settings or return to the main menu.</p>
<p>The biggest change came when I decided to create a settings panel for the first time. I wanted to add settings in order to make my game more user-friendly and professional. Currently, the settings are composed of a volume slider and a fullscreen toggle.</p>
<p>In the future, I would like to add in keyboard changes and an original design for the cursor. I have always known UI to be an important part of game design, but it wasn't until learning more of game design that I realized its purpose: to improve user accessibility.</p>
<div class="before-after"><div class="before-after__item"><p class="before-after__label">Whisker</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/08-whisker2-ui-inspiration/whisker.png" alt="Whisker UI" class="img_ev3q"></div><div class="before-after__item"><p class="before-after__label">Whisker 2</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/08-whisker2-ui-inspiration/whisker2.png" alt="Whisker 2 UI" class="img_ev3q"></div></div>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker-2" term="whisker-2"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="ui" term="ui"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Development Journey: What My First Playtest Taught Me About Game Design]]></title>
        <id>https://liuyisi.com/game-dev-notes/07-whisker-first-playtest</id>
        <link href="https://liuyisi.com/game-dev-notes/07-whisker-first-playtest"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[In the third week of COSMOS, we had our very first playtest that Friday.]]></summary>
        <content type="html"><![CDATA[<p>In the third week of COSMOS, we had our very first playtest that Friday.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/PUrH12igyJY?rel=0&amp;modestbranding=1" title="Whisker first playtest" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>Before we started, our instructors went over the instructions for running a playtest. We were told not to interfere at all with the playtesting process in order to get an authentic player experience. As the person with the most knowledge on how Whisker worked, I ran the playtests.</p>
<p>In the first round of playtests, our professors or our fellow clustermates could come and try out our games. I was personally very excited to show my game now that it was a complete prototype. However, almost as soon as they started playing, errors popped up. The game barely worked which surprised me as I had played through the game myself with almost no problems. The most glaring flaw was the lack of a tutorial: I had forgotten to include the details on how to play the game.</p>
<p>The playtest provided valuable input on my game and so by lunch time, I was already reworking my game. This time I made sure to include a written tutorial which would be given to the player through the dialogue, adding invisible walls, and minor fixes to improve the gameplay flow.</p>
<p>The second round of playtests were more successful, but I realized from this how much more I needed to learn!</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker" term="whisker"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="cosmos" term="cosmos"/>
        <category label="devlog" term="devlog"/>
        <category label="reflection" term="reflection"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why I Pivoted from Shooter to Cozy Game]]></title>
        <id>https://liuyisi.com/game-dev-notes/06-sharpshooter-to-baking-game</id>
        <link href="https://liuyisi.com/game-dev-notes/06-sharpshooter-to-baking-game"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[Although at first a shooter and a cooking game may not have a lot of similarities, it would turn out that a cozy baking game shares a lot of the same mechanics as my third-person shooter.]]></summary>
        <content type="html"><![CDATA[<p>Although at first a shooter and a cooking game may not have a lot of similarities, it would turn out that a cozy baking game shares a lot of the same mechanics as my third-person shooter.</p>
<p>Before we started producing our game for the final project, I was working solo to create a new game. I spent the first two weeks creating a new fighting game: zombie-smack. This game would be a zombie survival game where waves of zombies would come and attempt to harm the player. The player would be able to pick up rocks, and different types of sticks.</p>
<p>When we began creating our projects, I wanted to first see what I could reuse for my project to spend up development for the first playtest in 3 days. I wanted to make sure that I had a working prototype as soon as possible because my previous game development experience taught me that it takes a long time to make new code and how one error can break your game for days.</p>
<p>I immediately got to work redesigning my zombie-smack which was already a redesign of sharpshooter into Whisker. I borrowed the player mechanics: the player movement script was all good, but especially the throwing mechanic. This script allowed me to have my player be able to pick-up ingredients and throw them into different things. I readadjusted the code to make it so that the ingredient wouldn't fly so far away.</p>
<p>I changed the enemies to customers and by doing this I was able to get the player to pick-up an ingredient, make some sort of food with it, and serve it to a customer.</p>
<div class="before-after"><div class="before-after__item"><p class="before-after__label">Sharpshooter</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/06-sharpshooter-to-baking-game/sharpshooter.png" alt="Sharpshooter" class="img_ev3q"></div><div class="before-after__item"><p class="before-after__label">Zombie Smack</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/06-sharpshooter-to-baking-game/zombie-smack.png" alt="Zombie Smack" class="img_ev3q"></div><div class="before-after__item"><p class="before-after__label">Whisker</p><img decoding="async" loading="lazy" src="https://liuyisi.com/devlog/06-sharpshooter-to-baking-game/whisker.png" alt="Whisker" class="img_ev3q"></div></div>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="sharpshooter" term="sharpshooter"/>
        <category label="whisker" term="whisker"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
        <category label="design" term="design"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Cutting Half Your Features to Ship on Time]]></title>
        <id>https://liuyisi.com/game-dev-notes/05-whisker-cut-features</id>
        <link href="https://liuyisi.com/game-dev-notes/05-whisker-cut-features"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[On the first day of week 3 of COSMOS, we started our final project. My teammates and I both had ideas for how the project should be.]]></summary>
        <content type="html"><![CDATA[<p>On the first day of week 3 of COSMOS, we started our final project. My teammates and I both had ideas for how the project should be.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/5bryrIIJN5M?rel=0&amp;modestbranding=1" title="Whisker cut features devlog" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>But, it wasn't plausible for all the features we wanted to be included in a game that we only had 10 days to make. As a result, I used my previous game development knowledge to work with my teammates to group our ideas into a specific concept that was also doable with our timeframe.</p>
<p>Here are some of the ideas that were suggested, but never made it. We originally wanted the game to have a garden because the plot would involve someone stealing from it. Nevertheless, we couldn't do it because we hadn't even fleshed out a single level yet. Another feature would have been beverage orders. This didn't meet the cut because it would need much more art assets.</p>
<p>Although there were a lot of great features that didn't meet the cut, it did help our group finish our project much faster. We were the only group that consistently met our deadlines which gave us the time to polish our game.</p>
<p>Even though it was hard to say goodbye, it was definitely for the best.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="whisker" term="whisker"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="cosmos" term="cosmos"/>
        <category label="devlog" term="devlog"/>
        <category label="scope" term="scope"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Island Runner: Why I Chose to Remake My Map for My Infinite Runner]]></title>
        <id>https://liuyisi.com/game-dev-notes/04-island-runner-map-remake</id>
        <link href="https://liuyisi.com/game-dev-notes/04-island-runner-map-remake"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[When I remade my infinite runner for the first time, I used the traditional structure of creating different road chunks in order to allow the game to be, well, an infinite runner.]]></summary>
        <content type="html"><![CDATA[<p>When I remade my infinite runner for the first time, I used the traditional structure of creating different road chunks in order to allow the game to be, well, an infinite runner.</p>
<!-- -->
<p>Although the system worked well, I felt like I could try to spice up the original formula. This time I used a premade map and instead procedurally generated powerups and obstacles to form a path. This time I could explore more with my infinite runner paths as they no longer needed to be straight, but could be curved, uphill, downhill, and in a cave.</p>
<p>In the future, I would like to experiment more with map design because the new map I used wasn't something I made. But, I still feel that changing the map was a good choice because it allowed me to experiment more with the mechanics of my game.</p>
<div class="before-after"><div class="before-after__item"><p class="before-after__label">Original Map</p><iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/JtkAh8PlE48?rel=0&amp;modestbranding=1" title="Island Runner original map" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe></div><div class="before-after__item"><p class="before-after__label">Remade Map</p><iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/793Mcx-RJ_s?rel=0&amp;modestbranding=1" title="Island Runner new map" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe></div></div>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="infinite-runner" term="infinite-runner"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sharpshooter Devlog: Why I Chose to Have Birds be the Enemies in My Third-Person Shooter]]></title>
        <id>https://liuyisi.com/game-dev-notes/03-sharpshooter-bird-enemies</id>
        <link href="https://liuyisi.com/game-dev-notes/03-sharpshooter-bird-enemies"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[While I was working to rework my map for my sharpshooter game, I gradually realized that there were other things I wanted to change as well. The most glaring of which was the enemy sprites.]]></summary>
        <content type="html"><![CDATA[<p>While I was working to rework my map for my sharpshooter game, I gradually realized that there were other things I wanted to change as well. The most glaring of which was the enemy sprites.</p>
<p>For a bit of context, I originally followed a tutorial on how to make a first-person shooter in Unity; before, I decided that I wanted to make my own game. From there, I changed the theme of my game from sci-fi and futuristic which was based on the original tutorial to a green plain covered with hills, trees, a house, ice, water, more ice, and also a design entirely based on my own ideas. This was when I decided I should change my enemy sprite from a robot to a bird. I chose a bird specifically because I felt like my open green space should have an enemy that was down-to-earth, more natural, and maybe even a little silly since my game was in low-poly.</p>
<p>I found a free bird model off of the Unity asset store. It was a brown bird and had some animations for moving and flying. The bird enemy gave me more options for how the enemy would move as well. Because they had a flying animation, I felt it would be a great opportunity to spice up the attack patterns. I changed the code to allow the bird to fly around, circle around you, and try to peck at you when they got close enough.</p>
<p>The bird change may have been just a simple model switch, but it was the start of my independent game development journey.</p>
<div class="before-after"><div class="before-after__item"><p class="before-after__label">Original Map</p><iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/3vu86MJPu7Q?rel=0&amp;modestbranding=1" title="Sharpshooter original map" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe></div><div class="before-after__item"><p class="before-after__label">Remade Map</p><iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/v-OEeVP-HUI?rel=0&amp;modestbranding=1" title="Sharpshooter new map" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe></div></div>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="sharpshooter" term="sharpshooter"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Murder Mystery Devlog: How we came up with a murder mystery game for our final]]></title>
        <id>https://liuyisi.com/game-dev-notes/02-murder-mystery-devlog</id>
        <link href="https://liuyisi.com/game-dev-notes/02-murder-mystery-devlog"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[For the final of AP CSA, we split off into groups in order to make a project for our second semester final.]]></summary>
        <content type="html"><![CDATA[<p>For the final of AP CSA, we split off into groups in order to make a project for our second semester final.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/GdrQg1zcdtk?rel=0&amp;modestbranding=1" title="Murder Mystery game devlog" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>I quickly suggested that we make a murder mystery game for our project to my two other teammates and it was settled. I was very excited to use Unity for our project. It was originally supposed to be a game where you had to find a culprit except the murderer was randomized so that each time it would be someone different.</p>
<p>We originally planned for five different NPCs that could be the suspect and the possibility of you, the player, also having a chance to be on the other side. However, the plan quickly proved overtly ambitious so we had to scale down to 3 NPCs. Nevertheless, we were able to keep the core mechanic intact.</p>
<p>Overall, for my first group and original game, Murder Mystery is truly one to remember!</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="murder-mystery" term="murder-mystery"/>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Development Journey: How I made my first game and why]]></title>
        <id>https://liuyisi.com/game-dev-notes/01-how-i-made-my-first-game</id>
        <link href="https://liuyisi.com/game-dev-notes/01-how-i-made-my-first-game"/>
        <updated>2026-10-26T03:51:51.000Z</updated>
        <summary type="html"><![CDATA[The start of the game development journey with Unity would follow a set of tutorials.]]></summary>
        <content type="html"><![CDATA[<p>The start of the game development journey with Unity would follow a set of tutorials.</p>
<iframe width="100%" style="aspect-ratio:16/9;border:none" src="https://www.youtube-nocookie.com/embed/S9uz7BVrog0?rel=0&amp;modestbranding=1" title="How I made my first game" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p>The first game I made was an infinite runner which centered on a King who ran through medieval inspired road chunks. The game was a simple infinite runner that followed a tutorial, but it was really inspiring to be able to make such a complete product in only about 2 weeks of time. I felt so accomplished to see my work transform into something so tangible.</p>
<p>It was great that I could also add in my own flair to the game. I changed up the road chunks and worked on the post-processing.</p>
<p>Overall, the game was just one game, but it would inspire me to create my next one.</p>]]></content>
        <author>
            <name>Yisi Liu</name>
            <uri>https://github.com/yisliu</uri>
        </author>
        <category label="game-dev" term="game-dev"/>
        <category label="unity" term="unity"/>
        <category label="devlog" term="devlog"/>
        <category label="reflection" term="reflection"/>
    </entry>
</feed>