<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dilawar Hussain</title>
    <description>The latest articles on DEV Community by Dilawar Hussain (@dilawarhussain6465).</description>
    <link>https://dev.to/dilawarhussain6465</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4176303%2Fd7ae54b4-04bc-4b87-b26a-8f3a2182baff.png</url>
      <title>DEV Community: Dilawar Hussain</title>
      <link>https://dev.to/dilawarhussain6465</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dilawarhussain6465"/>
    <language>en</language>
    <item>
      <title>55+ Shipped Games Later — 10 Unity Lessons I Wish I Knew on Day One</title>
      <dc:creator>Dilawar Hussain</dc:creator>
      <pubDate>Sun, 11 Oct 2026 05:03:30 +0000</pubDate>
      <link>https://dev.to/dilawarhussain6465/55-shipped-games-later-10-unity-lessons-i-wish-i-knew-on-day-one-577i</link>
      <guid>https://dev.to/dilawarhussain6465/55-shipped-games-later-10-unity-lessons-i-wish-i-knew-on-day-one-577i</guid>
      <description>&lt;p&gt;Five years ago I thought being a good Unity developer meant knowing the API.&lt;/p&gt;

&lt;p&gt;Then I started shipping. Mobile puzzlers, a 16-player social game, a $550K strategy game, VR training simulators, a fintech event game built in under two weeks — 55+ titles across iOS, Android, PC, WebGL and Meta Quest.&lt;/p&gt;

&lt;p&gt;Shipping taught me things no tutorial did. Here are the 10 lessons that saved me the most time, bugs and sanity.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Prototype the fun before you architect anything
&lt;/h2&gt;

&lt;p&gt;The most expensive mistake in game dev is building a beautiful architecture for a game that isn't fun.&lt;/p&gt;

&lt;p&gt;Now I get a &lt;strong&gt;playable&lt;/strong&gt; version in front of people as fast as possible — grey boxes, placeholder UI, hardcoded values. If the core loop isn't fun with cubes, it won't be fun with art.&lt;/p&gt;

&lt;p&gt;When I set up a rapid-prototyping pipeline for my team, our time to first playable build dropped by about 30%. The trick wasn't a tool — it was permission to write throwaway code &lt;em&gt;on purpose&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rule: prototype code is allowed to be ugly. Production code is not. Never confuse the two.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Put your numbers in ScriptableObjects, not in code
&lt;/h2&gt;

&lt;p&gt;If a designer has to ask you to change an enemy's speed, your architecture is wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;CreateAssetMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Game/Enemy Config"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnemyConfig&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ScriptableObject&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;moveSpeed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3.5f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxHealth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;attackCooldown&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Enemy&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;EnemyConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;moveSpeed&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now designers tune the game in the Inspector, you create variants (&lt;code&gt;FastEnemy&lt;/code&gt;, &lt;code&gt;TankEnemy&lt;/code&gt;) without new code, and balancing stops being a ticket in your queue.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Stop allocating in the hot path — pool everything that spawns
&lt;/h2&gt;

&lt;p&gt;Garbage collection spikes are the #1 cause of "it stutters sometimes" on mobile. Bullets, particles, coins, floating damage numbers — if it spawns often, pool it. Unity has a built-in pool since 2021:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine.Pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BulletSpawner&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Bullet&lt;/span&gt; &lt;span class="n"&gt;prefab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ObjectPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Bullet&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ObjectPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Bullet&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class="n"&gt;createFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefab&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;actionOnGet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;actionOnRelease&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;actionOnDestroy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;defaultCapacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;maxSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Bullet&lt;/span&gt; &lt;span class="n"&gt;bullet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// bullet calls this when it's done&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same idea for strings in &lt;code&gt;Update&lt;/code&gt; (cache them), &lt;code&gt;GetComponent&lt;/code&gt; in loops (cache it in &lt;code&gt;Awake&lt;/code&gt;) and LINQ in per-frame code (avoid it).&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Profile on the worst device you support — not your dev machine
&lt;/h2&gt;

&lt;p&gt;Your laptop lies to you. The game that runs at 120 FPS in the Editor can run at 25 FPS on a three-year-old budget Android phone — which is exactly what a big chunk of your players own.&lt;/p&gt;

&lt;p&gt;My checklist before every mobile release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Profile a &lt;strong&gt;development build on a real low-end device&lt;/strong&gt;, never just the Editor&lt;/li&gt;
&lt;li&gt;Watch the &lt;strong&gt;GC Alloc&lt;/strong&gt; column in the Profiler — it should be near zero during gameplay&lt;/li&gt;
&lt;li&gt;Check draw calls and overdraw, especially with transparent UI and particles&lt;/li&gt;
&lt;li&gt;Test a &lt;strong&gt;30-minute session&lt;/strong&gt; — memory leaks don't show up in 2 minutes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Decide multiplayer authority on day one
&lt;/h2&gt;

&lt;p&gt;Multiplayer isn't a feature you "add later". It changes how every system is written.&lt;/p&gt;

&lt;p&gt;On a 16-player social game with proximity voice chat, the hardest bugs never came from the networking library — they came from &lt;strong&gt;unclear ownership&lt;/strong&gt;. Who moves this object? Who decides this quest is complete? Who wins when two players grab the same item?&lt;/p&gt;

&lt;p&gt;Before writing any netcode, write down for every piece of state:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Who owns it?&lt;/th&gt;
&lt;th&gt;How is it synced?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Player position&lt;/td&gt;
&lt;td&gt;Owning client&lt;/td&gt;
&lt;td&gt;Continuous sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Item pickup&lt;/td&gt;
&lt;td&gt;Master / server&lt;/td&gt;
&lt;td&gt;Request + confirm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Score&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Event on change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That one table has saved me more debugging hours than any library feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Put a backend in early — even a tiny one
&lt;/h2&gt;

&lt;p&gt;Firebase or PlayFab from week one gives you three superpowers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remote config&lt;/strong&gt; — tune difficulty, prices and rewards without shipping a new build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt; — find out where players actually quit, instead of guessing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaderboards and cloud saves&lt;/strong&gt; — features players expect, almost free to add&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Adding a backend at the end means retrofitting every save, every reward and every number. Adding it at the start means you can fix a bad level with a config change at 2 a.m. — without waiting for store review.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. For deadlines, cut scope — never quality
&lt;/h2&gt;

&lt;p&gt;I once built an event game for a fintech company's live event in Singapore: brief to live in &lt;strong&gt;under two weeks&lt;/strong&gt;, with leaderboards, video explainers and a quiz.&lt;/p&gt;

&lt;p&gt;It shipped on time because we cut ruthlessly: one game mode instead of three, one art style, one platform (WebGL — nothing to install at the event). What we did &lt;strong&gt;not&lt;/strong&gt; cut: input responsiveness, load time and the leaderboard working under load.&lt;/p&gt;

&lt;p&gt;Players forgive missing features. They don't forgive a game that feels broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Keep AI API keys out of your game client
&lt;/h2&gt;

&lt;p&gt;AI features are everywhere now — chat assistants, generated content, smart NPCs. The most common mistake I see: the OpenAI or Gemini key &lt;strong&gt;inside the build&lt;/strong&gt;. Anyone can extract it in minutes.&lt;/p&gt;

&lt;p&gt;Put a tiny server function between your game and the AI provider, and call that instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine.Networking&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AiClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Your own backend endpoint — it holds the real API key and enforces limits.&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-backend.example.com/ask"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="nf"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onReply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnityWebRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uploadHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UploadHandlerRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downloadHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DownloadHandlerBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetRequestHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendWebRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;onReply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;UnityWebRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downloadHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sorry, something went wrong."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server keeps the key secret, rate-limits each player and filters content. Your client stays dumb — and safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Code reviews are a speed tool, not a bureaucracy tool
&lt;/h2&gt;

&lt;p&gt;When I started running weekly code reviews while mentoring three junior developers, reported bugs across our sprints dropped by roughly 35%.&lt;/p&gt;

&lt;p&gt;Not because reviews catch every bug — but because they spread knowledge. After a month, everyone knew where the save system lived, why the pooling rules existed and which "quick fix" patterns we'd banned.&lt;/p&gt;

&lt;p&gt;Keep reviews small, frequent and kind. A 200-line review gets real feedback. A 2,000-line review gets "LGTM".&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Unity isn't the answer to everything — and that's fine
&lt;/h2&gt;

&lt;p&gt;Some of my favourite recent work isn't in Unity at all. For an AI sports app — selfie in, cinematic kit photos and celebration videos out — I used &lt;strong&gt;Flutter&lt;/strong&gt;, because the product was mostly screens, flows and AI services, not realtime 3D.&lt;/p&gt;

&lt;p&gt;The rule I use now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unity&lt;/strong&gt; when the product lives in 3D, AR/VR or realtime graphics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flutter&lt;/strong&gt; when it's mostly UI, forms, feeds and API calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both&lt;/strong&gt; when an app needs a 3D or AR moment inside a normal app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing when &lt;em&gt;not&lt;/em&gt; to use your favourite tool is a senior skill.&lt;/p&gt;




&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Prototype the fun first&lt;/li&gt;
&lt;li&gt;Data in ScriptableObjects&lt;/li&gt;
&lt;li&gt;Pool everything that spawns&lt;/li&gt;
&lt;li&gt;Profile on your worst device&lt;/li&gt;
&lt;li&gt;Decide multiplayer authority on day one&lt;/li&gt;
&lt;li&gt;Backend early&lt;/li&gt;
&lt;li&gt;Cut scope, never quality&lt;/li&gt;
&lt;li&gt;AI keys stay on the server&lt;/li&gt;
&lt;li&gt;Small, frequent code reviews&lt;/li&gt;
&lt;li&gt;Pick the right tool, not the familiar one&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;I'm currently building &lt;strong&gt;open-source AI tools for the Unity Editor&lt;/strong&gt; — follow me here if you want to see them first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the one Unity lesson you learned the hard way?&lt;/strong&gt; Drop it in the comments — I read every one. 👇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm Dilawar, a Senior Unity Engineer and Flutter &amp;amp; AI app developer. You can see the games, apps and XR projects behind these lessons at &lt;a href="https://www.dilawarhussain.site" rel="noopener noreferrer"&gt;dilawarhussain.site&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>unity3d</category>
      <category>ai</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
