<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Game Torrahod]]></title><description><![CDATA[อยากทำเกมต้องทรหดอดทน ฆ่าได้บัคไม่ได้]]></description><link>https://gametorrahod.com/</link><image><url>https://gametorrahod.com/favicon.png</url><title>Game Torrahod</title><link>https://gametorrahod.com/</link></image><generator>Ghost 5.16</generator><lastBuildDate>Thu, 23 Apr 2026 01:42:52 GMT</lastBuildDate><atom:link href="https://gametorrahod.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[AudioResource, AudioClip, AudioRandomContainer Interactions]]></title><description><![CDATA[<p>Long time no see. I&apos;m currently trying to make a SFX player audio plugin based on <code>AudioResource</code>, a new base class that covers both <code>AudioClip</code> and <code>AudioRandomContainer</code> (Unity 6 addition). &#xA0;I have several questions that the document didn&apos;t go into details that I had to</p>]]></description><link>https://gametorrahod.com/audio-random-container/</link><guid isPermaLink="false">68e74393981d500594735f49</guid><dc:creator><![CDATA[Sirawat Pitaksarit / 5argon]]></dc:creator><pubDate>Thu, 09 Oct 2025 10:47:27 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1753898854513-97ea15851501?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8YWxsfDR8fHx8fHx8fDE3NjAwMDYwNjR8&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1753898854513-97ea15851501?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8YWxsfDR8fHx8fHx8fDE3NjAwMDYwNjR8&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="AudioResource, AudioClip, AudioRandomContainer Interactions"><p>Long time no see. I&apos;m currently trying to make a SFX player audio plugin based on <code>AudioResource</code>, a new base class that covers both <code>AudioClip</code> and <code>AudioRandomContainer</code> (Unity 6 addition). &#xA0;I have several questions that the document didn&apos;t go into details that I had to research myself. I hope this article can impart this niche knowledge to those interested.</p><p>I&apos;ll start with reiterating prior behaviours first. (The gang consists of : <code>AudioMixer</code> <code>AudioMixerGroup</code> <code>AudioSource</code> <code>AudioClip</code>) &#xA0;Then I&apos;ll get to the <code>AudioResource</code> and <code>AudioRandomContainer</code> which is our new stuff whether they conform to the previous functions or not.</p><p>Small notes : There are graph time scheduling bug on <code>AudioRandomContainer</code> plays when I test it on 6000.0.50f1, where pausing the game time or the editor will mess with its pulse/offset feature causing it to &quot;miss&quot; the schedule. It seemed to be fixed in 6000.0.59f2. So if you are still on older version please update before playing with it.</p><h2 id="test-set">Test Set</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-1.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="986" height="818" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-1.png 600w, https://gametorrahod.com/content/images/2025/10/image-1.png 986w" sizes="(min-width: 720px) 720px"></figure><p>For audio memory testing, I&apos;ll use this long audio file with 10 MB original size and 3.8 MB compressed size. And I&apos;ll change up Load Type and Preload Audio Data on each study.</p><p>I also have a shorter 1 second file when the test requires a play to end.</p><h2 id="audiomixer-and-profiler">AudioMixer and Profiler</h2><p>In a 2 <code>AudioMixer</code> setup <code>MainMixer</code> + <code>SubMixer</code>, where <code>MainMixer</code> has several of its own <code>AudioMixerGroup</code>, and one called &quot;From SubMixer&quot; that gets the audio from <code>SubMixer</code>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-6.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1932" height="1302" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-6.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-6.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-6.png 1600w, https://gametorrahod.com/content/images/2025/10/image-6.png 1932w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-7.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1446" height="1292" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-7.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-7.png 1000w, https://gametorrahod.com/content/images/2025/10/image-7.png 1446w" sizes="(min-width: 720px) 720px"></figure><p>They are presented in the Profiler like this (showing 4 play one shots currently playing under one of the mixer group) :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-8.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1988" height="534" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-8.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-8.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-8.png 1600w, https://gametorrahod.com/content/images/2025/10/image-8.png 1988w" sizes="(min-width: 720px) 720px"></figure><h2 id="audioclip-memory">AudioClip Memory</h2><p>If scene has asset reference that eventually leads to <code>AudioClip</code> and it has Preload Audio Data, then it takes memory immediately. How much depends on Load Type. Streaming Load Type cannot have Preload Audio Data.</p><p>If Preload Audio Data is not checked, then it is loaded on its first play.</p><p><code>audioClip.UnloadAudioData()</code> can unload audio that was loaded with Preload Audio Data.</p><p><code>Resources.UnloadUnusedAssets()</code> can work similarly to <code>audioClip.UnloadAudioData()</code> without the need to have <code>AudioClip</code> object reference. But the definition of &quot;used&quot; is by following objects in memory, and that means everything in the hierarchy are considered used. <code>Resources.UnloadUnusedAssets()</code> cannot unload the audio memory if some <code>GameObject</code> is still referencing it, even not actually playing it.</p><h2 id="audiosourceplayoneshot">AudioSource.PlayOneShot</h2><p>The argument is still <code>AudioClip</code>, not <code>AudioResource</code>. Therefore you cannot use <code>RandomAudioContainer</code> this way. Understandable, because it is possible to configure <code>RandomAudioContainer</code> to be infinitely repeating according to the playlist.</p><p>In the Profiler, stacked <code>PlayOneShot</code> of the same long <code>AudioClip</code> shows up as new &quot;Audio Voices&quot; &#xA0;without incrementing &quot;Total Audio Sources&quot;.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-2.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="359" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-2.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-2.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-2.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-2.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>By the way, this &quot;Audio Voices&quot; is what will be subjected to culling according to Max Real Voices in the Project Settings.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-9.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1472" height="826" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-9.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-9.png 1000w, https://gametorrahod.com/content/images/2025/10/image-9.png 1472w" sizes="(min-width: 720px) 720px"></figure><p>If <code>audioClip.UnloadAudioData()</code> is called while <code>PlayOneShot</code> of that <code>AudioClip</code> is playing, it automatically stops all such one shot instances and the memory is unloaded.</p><p>While one shot audio are ongoing, changing <code>AudioSource</code>&apos;s Volume, Pitch, <code>AudioMixerGroup</code> output will live update all of the running one shot instances. This shows how I can move all the one shots to another place in the mixer tree at once.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-3.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1890" height="552" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-3.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-3.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-3.png 1600w, https://gametorrahod.com/content/images/2025/10/image-3.png 1890w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-4.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="476" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-4.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-4.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-4.png 1600w, https://gametorrahod.com/content/images/2025/10/image-4.png 2186w" sizes="(min-width: 720px) 720px"></figure><p>On Load Type : Streaming, it seems to take fixed amount of memory (124 KB) to stream the audio no matter how long it is. It has a disadvantage that each stacking one shot instance will need its own 124 KB to stream concurrently, compared to Compressed in Memory or Decompress on Load that each instance can share the same memory. The 2nd image shows that streaming memory is now 248 KB for 2 consecutive streaming plays.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-10.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="834" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-10.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-10.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-10.png 1600w, https://gametorrahod.com/content/images/2025/10/image-10.png 2168w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-11.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="771" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-11.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-11.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-11.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-11.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="audiorandomcontainer">AudioRandomContainer</h2><p>With a default <code>AudioRandomContainer</code> that plays 2 clips back to back:</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-12.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="748" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-12.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-12.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-12.png 1600w, https://gametorrahod.com/content/images/2025/10/image-12.png 2012w" sizes="(min-width: 720px) 720px"></figure><p>The Profiler shows interesting technical aspect that is happening under the hood. </p><p>For any <code>AudioSource</code> that had played <code>RandomAudioContainer</code> even once, it starts managing an invisible audio playables called <code>AudioPlayable Group</code>, revealing that the &quot;backend&quot; of <code>RandomAudioContainer</code> is probably playable graphs. If I attempt to change <code>AudioMixerGroup</code>, even if <code>AudioResource</code> is removed from the slot, the Profiler shows that this <code>AudioPlayable Group</code> is being moved to different part of the tree automatically. &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-18.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="718" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-18.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-18.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-18.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-18.png 2400w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-19.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="693" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-19.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-19.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-19.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-19.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Unlike Play One Shot or Play on <code>AudioClip</code>, the Object name is blank for any plays that occured within <code>AudioRandomContainer</code>. This image below shows &quot;Channels&quot; mode without the groups so you can see that the name is blank. I play both <code>AudioClip</code> way and <code>AudioRandomContainer</code> way at the same time. </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/Screenshot-2568-10-09-at-13.57.03.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1593" height="567" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/Screenshot-2568-10-09-at-13.57.03.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/Screenshot-2568-10-09-at-13.57.03.png 1000w, https://gametorrahod.com/content/images/2025/10/Screenshot-2568-10-09-at-13.57.03.png 1593w" sizes="(min-width: 720px) 720px"></figure><p>Moreover, pausing Unity editor will remove the blank name channels from the Profiler on that frame. Because inspecting the Profiler automatically pauses the editor, you need to move the frame head back by one frame to see the audio played as a part of <code>AudioRandomContainer</code> you want to see.</p><p>It appears that there is a short moment where Audio Voices count is 2 while transitioning to the next clip, despite the container didn&apos;t allow the clips to overlap.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-13.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="816" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-13.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-13.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-13.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-13.png 2400w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-14.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="906" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-14.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-14.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-14.png 1600w, https://gametorrahod.com/content/images/2025/10/image-14.png 2288w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-15.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="875" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-15.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-15.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-15.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-15.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>One more interesting thing is that, while the first sound nest the channel under <code>AudioPlayable Group</code>, at the overlapping moment, and even after, you can see there are one more nested layer of <code>AudioPlayable Group</code>. It seems there is a complex logic that decide how many playable graph group it needs under the hood to run. It should not affect performance, just make this tree a little bit cluttered.&#x2003;</p><p>If we allow short break on the transition, the Profiler now shows a more expected graph which the Audio Voices dropped from 1 to 0 and then back to 1 when the next clip plays.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-16.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1010" height="316" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-16.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-16.png 1000w, https://gametorrahod.com/content/images/2025/10/image-16.png 1010w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-17.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1200" height="902" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-17.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-17.png 1000w, https://gametorrahod.com/content/images/2025/10/image-17.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>If you are going crazy on back to back plays, it might worth keeping in mind that short instance where Audio Voices are bloated, just in case you might be close to hitting the limit.</p><p><code>audioSource.Pause()</code> and <code>Unpause()</code> works as expected, if you pause and unpause repeatedly, it will go forward in the sequence bit by bit, including the wait time.</p><p>As for memory usage, it seems to do it clip by clip as expected. For example, I have sequential ARC of 10 MB each like this, with Preload Audio Data as false. It correctly starts as neither loaded. And when the ARC is played, only the first one is loaded because the 2nd is scheduled to be played with offset after the first one.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-20.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1052" height="258" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-20.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-20.png 1000w, https://gametorrahod.com/content/images/2025/10/image-20.png 1052w" sizes="(min-width: 720px) 720px"></figure><p>The <code>audioSource.Play()</code> while ARC&apos;s Trigger Mode is Manual has a self-overlaying effect similar to <code>PlayOneShot</code>. This is very different from <code>.Play()</code> while the asset is <code>AudioClip</code> which when called repeatedly, it would just abruptly stop and restart the clip. This example shows <code>.Play()</code> called consecutively 4 times, while Trigger is Manual and Playback is Sequential. It results in C and E graphs being added 2 times each. (4 total) Basically <code>.Play()</code> fires a trigger once.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-22.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1054" height="598" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-22.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-22.png 1000w, https://gametorrahod.com/content/images/2025/10/image-22.png 1054w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-21.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="2000" height="444" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-21.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-21.png 1000w, https://gametorrahod.com/content/images/size/w1600/2025/10/image-21.png 1600w, https://gametorrahod.com/content/images/size/w2400/2025/10/image-21.png 2400w" sizes="(min-width: 720px) 720px"></figure><p><code>.Pause()</code> and <code>.Unpause()</code> is able to altogether pause all overlapping instances and resume them in one go. Pretty cool actually.</p><p>When Trigger is Automatic, it works more like it was previously. Repeated <code>Play()</code> call will restart the whole playlist. For example if it is halfway through &quot;E&quot; and <code>Play()</code> was called again, the E audio abruptly stops and it starts over from the beginning of C.</p><p><code>AudioRandomContainer</code> is also currently not very extensible. The class is <code>internal</code> unlike <code>AudioResource</code> and <code>AudioClip</code>. And attempting to use reflection or direct <code>.asset</code> file writing might be brittle because they don&apos;t want you to do that. This is a YAML file of an ARC. Each element added to the <code>AudioClip</code> array is in fact not just <code>AudioClip</code> but <code>AudioContainerElement</code>, because it needs to hold adjustable volume and enabled checkbox per entry as well. Notice how <code>m_Element</code> links to each <code>AudioContainerElement</code> with <code>fileID</code>. (<a href="https://unity.com/blog/engine-platform/understanding-unitys-serialization-language-yaml">Related article</a>)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2025/10/image-23.png" class="kg-image" alt="AudioResource, AudioClip, AudioRandomContainer Interactions" loading="lazy" width="1584" height="1686" srcset="https://gametorrahod.com/content/images/size/w600/2025/10/image-23.png 600w, https://gametorrahod.com/content/images/size/w1000/2025/10/image-23.png 1000w, https://gametorrahod.com/content/images/2025/10/image-23.png 1584w" sizes="(min-width: 720px) 720px"></figure><p>Because <code>AudioRandomContainer</code> is entirely <code>internal</code> you lose control on <code>AudioClip</code> that it is made of, for example what if you want to <code>audioClip.UnloadAudioData()</code> to everything that&apos;s in the playlist of <code>AudioRandomContainer</code>?</p><p>The manual loading and unloading of non-Preload Audio Data clip I think is a critical feature in competent game. In loading screen I&apos;d load all the expected audio so it doesn&apos;t introduce unwanted hiccups. The most memorable case for the game I worked on is level up SFX. It&apos;s not often that the player leveled up, but it is a very important feedback sound. The fanfare is also quite long so if I didn&apos;t preload it, it is guaranteed to skip a few frames of beautiful level up animation. Now imagine that if I went all in to <code>RandomAudioContainer</code> and have a level up audio that would be randomized from 2-3 variations. &#xA0;Because the API provide no &#xA0;<code>public</code> way to access those 2-3 <code>AudioClip</code> from <code>AudioRandomContainer</code> , I&apos;d need to separately have reference to them elsewhere, or use reflections. This will quickly complicate things when the game has 300+ sounds each with 2~3 randomized variations.</p><h2 id="what-about-my-plugin">What about my plugin?</h2><p>Around 2022 to 2024, I have been developing an audio plugin where it plays everything with audio playable graph. It supports volume, pitch, sequencing, start midway, and chaining up audio playable assets endlessly, etc. It&apos;s almost the same feature set you see in <code>AudioRandomContainer</code> right now. I see right away because you are not supposed to be able to play multiple audio with different pitches on <code>AudioSource</code> because adjusting that affects every currently playing audio in real time, unless you do it as playable graph and use the pitch argument in one of the node that&apos;s on different branch from the others that goes into the mixer.</p><p>My plugin uses concept similar to <code>AudioRandomContainer</code>, able to wrap each <code>AudioClip</code> in a new container that allows per-clip volume adjustment, randomized play, and whatnot. And the name is more generic <code>AudioSet</code> / <code>AudioObject</code> because sometimes it is not supposed to random things. But it has editor tooling to automatically wraps every single <code>AudioClip</code> into the wrapper asset. As a bonus, if you named your audio clips like <code>Foo-1</code> <code>Foo-2</code> <code>Foo-3</code> you get only one <code>Foo</code> wrapped asset that is already setup to randomize the play between 3 versions. It also has a base serialilzable class just like <code>AudioResource</code>.</p><p>I took like 1~2 years to finish the plugin, <strong>only to discover that </strong>in production use when I put my playable-backed SFX slinging engine to test I was flooded by undebuggable FMOD warnings and sometimes runtime error. I had to pivot my plugin hard so everything is played by the old <code>PlayOneShot</code>. Many code documentation about making regular person understand what playable graphs are were already written however, that I have to soon discard them.</p><p>Today now that I discovered <code>AudioRandomContainer</code> will end up connecting the audio graph I&apos;m not having very good feeling about this. <code>AudioRandomContainer</code> looks great even if to just wrap a single <code>AudioClip</code> just so you could access its per-asset volume attenuation. But can I really bet my entire game&apos;s audio on this?</p><p>At one time (2 years ago?) the plugin wire up playable graph like how ARC is doing here. But Unity is so buggy with it (e.g. FMOD error spam I can&apos;t make any sense when a lot of audio is played through playable system.) It is clearly not something I can sell to anyone. I actually had my own cases that was unresolved, but since they decommissioned fogbugz and I didn&apos;t capture any screenshot I have nothing to show you other than these.</p><ul><li><a href="https://fogbugz.unity3d.com/default.asp?1259935_h4lbeo17t8685ksl">https://fogbugz.unity3d.com/default.asp?1259935_h4lbeo17t8685ksl</a></li><li><a href="http://fogbugz.unity3d.com/default.asp?1167289_7t1ofosrlthhs3eo">http://fogbugz.unity3d.com/default.asp?1167289_7t1ofosrlthhs3eo</a></li></ul><p>I had to implement &quot;compatibility mode&quot; that turns the entire plugin into essentially <code>PlayOneShot</code> machine. (This was before the plugin was named &quot;One Shot Framework&quot; and more like SFX Framework) Then life happened, fast forwarded to Unity 6 they came out with what was said in this article. Now, I want to continue developing this plugin, I&apos;m thinking should I use <code>AudioRandomContainer</code> instead of &quot;my class&quot; for the wrapper? Or at least base my base class on <code>AudioResource</code> instead of my own?</p><p>After this entire research, the answer is likely no. The whole thing looks feature rich, but inextensible and I had trust issue to commit into it with reflections and direct <code>.asset</code> file writing tricks.</p><p>Slightly related, when I test it (2 years ago?) anything audio playables are broken in WebGL build despite this page <a href="https://docs.unity3d.com/Manual/webgl-audio.html">https://docs.unity3d.com/Manual/webgl-audio.html</a> saying nothing about them. By the way, that page also promises <code>PlayScheduled</code> being supported in WebGL build, which my plugin <a href="http://exceed7.com/introloop">Introloop</a> heavily uses, but schedule ended up buggy and unusable. </p><h2 id="my-evaluation">My Evaluation</h2><p><code>AudioRandomContainer</code> looks very feature-rich and almost made me wanted to wrap every single <code>AudioClip</code> in my project into <code>AudioRandomContainer</code>. But the opaqueness of the class and how it is playable-backed made me hesitate. The asset being only able to create one by one by hand reliably also discourage me from using it. What if I spent time setting everything up in ARCs and now I found the same FMOD errors later? </p><p>For now I will <strong>recreate </strong>a similar <code>AudioClip</code> wrapping system, except that it should eventually leads to the battle tested <code>PlayOneShot</code>. It&apos;s too risky to go all in on ARCs right now. The first step I&apos;d like to see from Unity would be making the class not <code>internal</code>. And at least each <code>AudioContainerElement</code> should be accessible by script.</p>]]></content:encoded></item><item><title><![CDATA[ECS Programming Patterns from Official Packages]]></title><description><![CDATA[We can learn interesting programming patterns from Unity's own packages that references Unity.Entities.]]></description><link>https://gametorrahod.com/ecs-patterns/</link><guid isPermaLink="false">66473241981d500594735cd4</guid><category><![CDATA[Unity DOTS]]></category><dc:creator><![CDATA[Sirawat Pitaksarit / 5argon]]></dc:creator><pubDate>Mon, 20 May 2024 18:38:51 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1576502200916-3808e07386a5?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDcyfHxwYXR0ZXJufGVufDB8fHx8MTcxNjIzMDEwN3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1576502200916-3808e07386a5?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDcyfHxwYXR0ZXJufGVufDB8fHx8MTcxNjIzMDEwN3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="ECS Programming Patterns from Official Packages"><p>Unity DOTS is still young so it is rather unclear what other people are doing when trying to accomplish a task. Video tutorials and sample projects are out there but can be hard to gather and verify whether they are the best practice or not.</p><p>There is one place we can learn from that has high confidence of being a good pattern, it&apos;s any Unity&apos;s own packages that references <code>Unity.Entities</code>. This is such as the graphics, the transform, the physics, etc. that the code is surely up to date (because otherwise they wouldn&apos;t compile with their own <code>Unity.Entities</code>).</p><p><strong>Note : I&apos;ll have to keep coming back to update this article as I run into new patterns, since it is impossible to read all codes in all packages just to write this article.</strong></p><h2 id="cleanup-component-to-detect-other-components-removal">Cleanup component to detect other component&apos;s removal</h2><p>It is stated in the documentation that &quot;This is useful to tag entities that require cleanup when destroyed.&quot;. It can be also interpreted in this way : You want to trigger a code when a specific component is gone from both reasons : component remove + entity is destroyed. You create a cleanup component to pair with it.</p><p>Use query<code>WithNone&lt;RealComponent&gt;.WithAll&lt;TheCleanupComponent&gt;</code> detects both ways. Without the cleanup component, just <code>WithNone&lt;RealComponent&gt;</code> would only cover a component&apos;s removal and not destroying the entity.</p><p>In this usage, you may want to know the content of the component that was just gone. But since it is gone you can&apos;t access it now! This is when <code>ICleanupComponent</code> should start having some data from the real component you want to know the last status when it is gone. You will also need to keep the <code>ICleanupComponent</code> up to date with the real one.</p><p>In <code>Unity.Transforms</code> package, <code>Parent</code> <code>IComponentData</code> has a pair <code>PreviousParent</code> <code>ICleanupComponentData</code>. There is a work to do on the entity that was the parent of someone, whenever the <code>Parent</code> is removed or when entity with <code>Parent</code> is destroyed.</p><h2 id="per-entity-change-checking">Per-entity change checking</h2><p>Check out this article on the bottom : </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://gametorrahod.com/change-version/#advanced-per-entity-change-checking"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Chunk&#x2019;s Change Version</div><div class="kg-bookmark-description">DOTS comes with a per-chunk versioning and a related filter feature. This feature can optimize processing the data only when it mattered.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://gametorrahod.com/content/images/size/w256h256/2019/03/1_lwYPVhKwFBvicoKoqM3Lrw@2x-2.png" alt="ECS Programming Patterns from Official Packages"><span class="kg-bookmark-author">Game Torrahod</span><span class="kg-bookmark-publisher">Sirawat Pitaksarit / 5argon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1715415272375-1d0c8ef29b5b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8YWxsfDIwfHx8fHx8Mnx8MTcxNTYxODE4NXw&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="ECS Programming Patterns from Official Packages"></div></a></figure><h2 id="tuple-array">Tuple array</h2><!--kg-card-begin: markdown--><pre><code class="language-csharp">void AddChildrenToParent(Entity parent, DynamicBuffer&lt;Child&gt; children)
{
    if (ParentChildrenToAdd.TryGetFirstValue(parent, out var child, out var it))
    {
        do
        {
            children.Add(new Child() { Value = child });
        }
        while (ParentChildrenToAdd.TryGetNextValue(out child, ref it));
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>The <code>NativeParallelMultiHashMap</code> from <code>Unity.Collections</code> is &quot;multi&quot; in that duplicate keys are fine. And therefore it could be used as tuple array.</p><p>Use <code>if</code> + <code>TryGetFirstValue</code> + <code>do</code> + (<code>while</code> + <code>TryGetNextValue</code>) to iterate.</p><h2 id="helper-to-add-a-set-of-related-components">Helper to add a set of related components</h2><p>Unlike <code>MonoBehaviour</code>, often one <code>IComponentData</code> won&apos;t do anything on its own. Programmer design components much more modularly due to the query feature and linear memory concern, systems often ask for a big set of <code>IComponentData</code> to start working.</p><p>How <code>Unity.Transforms</code> and Entities Graphics turns out, user would need to read documentation to learn what components are the entry point to get the systems running, and what components are automatically added after. Such entry point components could use some helping hand from the system programmer. </p><p>An example of how to do this comes from <code>RenderMeshUtility.AddComponents</code> in Entities Graphcis package. Send in <code>Entity</code> and <code>EntityManager</code>, and as you expected the inside are a bunch of add components.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public static void AddComponents(
    Entity entity,
    EntityManager entityManager,
    in RenderMeshDescription renderMeshDescription,
    RenderMeshArray renderMeshArray,
    MaterialMeshInfo materialMeshInfo = default)
{
    // ...
}
</code></pre>
<!--kg-card-end: markdown--><p>Inside contains a dynamic <code>ComponentTypeSet</code> generator that has more or less types depending on flags coming in. The <code>ComponentTypeSet</code> is compatible to overload <code>AddComponent(Entity entity, in ComponentTypeSet componentTypeSet)</code>. It is created with <code>FixedList128Bytes&lt;ComponentType&gt;</code> and doesn&apos;t need manual disposing.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">var components = new FixedList128Bytes&lt;ComponentType&gt;
{
    // Absolute minimum set of components required by Entities Graphics
    // to be considered for rendering. Entities without these components will
    // not match queries and will never be rendered.
    ComponentType.ReadWrite&lt;WorldRenderBounds&gt;(),
    ComponentType.ReadWrite&lt;RenderFilterSettings&gt;(),
    ComponentType.ReadWrite&lt;MaterialMeshInfo&gt;(),
    ComponentType.ChunkComponent&lt;ChunkWorldRenderBounds&gt;(),
    ComponentType.ChunkComponent&lt;EntitiesGraphicsChunkInfo&gt;(),
    // Extra transform related components required to render correctly
    // using many default SRP shaders. Custom shaders could potentially
    // work without it.
    ComponentType.ReadWrite&lt;WorldToLocal_Tag&gt;(),
    // Components required by Entities Graphics package visibility culling.
    ComponentType.ReadWrite&lt;RenderBounds&gt;(),
    ComponentType.ReadWrite&lt;PerInstanceCullingTag&gt;(),
};
</code></pre>
<!--kg-card-end: markdown--><h2 id="a-system-to-ensure-related-components-exist">A system to ensure related components exist</h2><p>Related to the previous entry, you might instead document that user need to add one component (so you don&apos;t need a helper to add multiple components at once) and others will automatically appear. The Entities Graphics package do so by making a system which has only one work of doing the add component by <code>WithAll&lt;ThatOneComponent&gt;.WithNone&lt;ToAdd&gt;</code> queries.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">protected override void  OnUpdate()
{
    EntityManager.AddComponent(m_MissingRootLODRange, typeof(RootLODRange));
    EntityManager.AddComponent(m_MissingRootLODWorldReferencePoint, typeof(RootLODWorldReferencePoint));
    EntityManager.AddComponent(m_MissingLODRange, typeof(LODRange));
    EntityManager.AddComponent(m_MissingLODWorldReferencePoint, typeof(LODWorldReferencePoint));
    EntityManager.AddComponent(m_MissingLODGroupWorldReferencePoint, typeof(LODGroupWorldReferencePoint));
}

</code></pre>
<!--kg-card-end: markdown--><p>With this design, those supporting components will be resistant to forced removal since they will immediately come back, and immediately got their value recomputed by an another system that keeps updating their value.</p><h2 id="reusable-groups-of-archetypes">Reusable groups of archetypes</h2><!--kg-card-begin: markdown--><pre><code class="language-csharp">internal struct ResolveSceneSectionArchetypes
{
  public EntityArchetype SectionEntityRequestLoad;
  public EntityArchetype SectionEntityNoLoad;
}

internal static ResolveSceneSectionArchetypes CreateResolveSceneSectionArchetypes(EntityManager manager)
{
    return new ResolveSceneSectionArchetypes
    {
        SectionEntityRequestLoad = manager.CreateArchetype(
            typeof(RequestSceneLoaded),
             typeof(SceneSectionData),
            typeof(SceneBoundingVolume),
            typeof(SceneEntityReference),
            typeof(ResolvedSectionPath)),
        SectionEntityNoLoad = manager.CreateArchetype(
            typeof(SceneSectionData),
            typeof(SceneBoundingVolume),
            typeof(SceneEntityReference),
            typeof(ResolvedSectionPath))
    };
}
</code></pre>
<!--kg-card-end: markdown--><p>Taken from <code>Unity.Scenes</code>, it needs to use this set of archetypes in multiple places. Since you need <code>EntityManager</code> to create an archetype at each place it wants to use, you can create a <code>static</code> helper to create the archetype with <code>EntityManager</code> as input.</p><h2 id="disable-tag-component">Disable tag component</h2><!--kg-card-begin: markdown--><pre><code class="language-csharp">m_PendingStreamRequests = new EntityQueryBuilder(Allocator.Temp)
    .WithAllRW&lt;RequestSceneLoaded, SceneSectionData&gt;()
    .WithAllRW&lt;ResolvedSectionPath&gt;()
    .WithNone&lt;StreamingState, DisableSceneResolveAndLoad&gt;()
    .Build(this);
m_UnloadStreamRequests = new EntityQueryBuilder(Allocator.Temp)
    .WithAllRW&lt;StreamingState&gt;()
    .WithAll&lt;SceneSectionData,SceneEntityReference&gt;()
    .WithNone&lt;RequestSceneLoaded, DisableSceneResolveAndLoad&gt;()
    .Build(this);
m_NestedScenesPending = new EntityQueryBuilder(Allocator.Temp)
    .WithAllRW&lt;RequestSceneLoaded, SceneTag&gt;()
    .WithNone&lt;StreamingState, DisableSceneResolveAndLoad&gt;()
    .Build(this);
</code></pre>
<!--kg-card-end: markdown--><p>This is a simple pattern of creating a tag component to avoid getting worked on by a system (but keep all other components, so as soon as you remove this component it resumes work), by putting it in all <code>WithNone</code> of every queries. They can be next to actual <code>WithNone</code> components that the queries are actually interested in.</p><p>This strategy is good when queries are clearly grouped together neatly in <code>OnCreate</code> and component name perhaps starts with <code>Disable</code> so it self-document.</p><p>It is from <code>Unity.Scenes</code> where (I think) when editor-only code load a scene it would need a special component to prevent it getting worked on like at runtime.</p><h2 id="request-component-for-reversible-operation">Request component for reversible operation</h2><p>In <code>Unity.Scenes</code>, they use a pattern of attaching a component <code>RequestSceneLoaded</code> to a scene entity and their section entities will be loaded. Also when you remove this component, their section entities are unloaded.</p><p>You order something to occur with a tag component attaching, and order it to be reversed when the component is removed. Component being able to be added only once helps with work doesn&apos;t make sense to occur twice, but make sense to be able to occur again after it is reversed.</p><p>This design is different from &quot;request entity&quot; where you create an entity with a component to order a system to do work, then that system try to flag the work as done either by tagging it, using enableable component, or destroying the entity. This design allows the work to occur repeatedly.</p><h2 id="using-on-allocator"><code>using</code> on Allocator&#x2003;</h2><!--kg-card-begin: markdown--><pre><code class="language-csharp">private void RemoveSceneEntities(EntityQuery query)
{
    if (!query.IsEmptyIgnoreFilter)
    {
        using (var removeEntities = query.ToEntityArray(Allocator.TempJob))
        using (var removeGuids =
               query.ToComponentDataArray&lt;AssetDependencyTrackerState&gt;(Allocator.TempJob))
        {
            for (int i = 0; i != removeEntities.Length; i++)
            {
                LogResolving(&quot;Removing&quot;, removeGuids[i].SceneAndBuildConfigGUID);
                _AssetDependencyTracker.Remove(removeGuids[i].SceneAndBuildConfigGUID, removeEntities[i]);
            }
        }

        EntityManager.RemoveComponent&lt;AssetDependencyTrackerState&gt;(query);
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>If you are going to use an allocator that requires manual <code>.Dispose</code>, then <code>using</code> block is an option to organize your code.</p>]]></content:encoded></item><item><title><![CDATA[Thinking in Cache]]></title><description><![CDATA[This article might help you picture the cache while you code DOTS. It is much more enjoyable to code data-oriented when you know what's going on.]]></description><link>https://gametorrahod.com/thinking-in-cache/</link><guid isPermaLink="false">66431e25981d5005947352d1</guid><category><![CDATA[Unity DOTS]]></category><dc:creator><![CDATA[Sirawat Pitaksarit / 5argon]]></dc:creator><pubDate>Tue, 14 May 2024 13:11:49 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1712522809850-6061aa856a45?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDExfHxjYWNoZXxlbnwwfHx8fDE3MTU2OTIyMjB8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1712522809850-6061aa856a45?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDExfHxjYWNoZXxlbnwwfHx8fDE3MTU2OTIyMjB8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Thinking in Cache"><p>This article might help you picture the cache while you code DOTS. While you could get by without constantly being aware of cache, it is much more enjoyable to code data-oriented when you know what&apos;s going on.</p><p>Some component definitions :</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public struct ComponentA : IComponentData
{
    public int Data;
}

public struct ComponentB : IComponentData
{
    public int Data;
}

public struct ComponentC : IComponentData
{
    public int Data;
}
</code></pre>
<!--kg-card-end: markdown--><p>If iterating per entity where you have component variable of one entity coming in each time, you can write it either in <code>SystemAPI.Query</code> and <code>IJobEntity</code> like so :</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">foreach (
    var (a, b, c) in
    SystemAPI.Query&lt;RefRO&lt;ComponentA&gt;, RefRW&lt;ComponentB&gt;, RefRO&lt;ComponentC&gt;&gt;()
)
{
    b.ValueRW.Data += a.ValueRO.Data + c.ValueRO.Data;
}
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><pre><code class="language-csharp">public partial struct MyJob : IJobEntity
{
    public void Execute(in ComponentA a, ref ComponentB b, in ComponentC c)
    {
        b.Data += a.Data + c.Data;
    }
}
</code></pre>
<!--kg-card-end: markdown--><h2 id="always-backed-by-a-linear-array">Always backed by a linear array</h2><p>It is important to understand that the <code>a</code>, <code>b</code>, and <code>c</code> in both styles <strong>points to</strong> somewhere in a linear native array of just that component. The array of next component comes only after the final element of first component. This is roughly how the chunk looks like.</p><pre><code class="language-csharp">// The first Execute
[a, a, a, ..., a][b, b, b, ..., b][c, c, c, ..., c]
 ^                ^                ^
 
// The 2nd Execute
[a, a, a, ..., a][b, b, b, ..., b][c, c, c, ..., c]
    ^                ^                ^
    
// The 3rd Execute
[a, a, a, ..., a][b, b, b, ..., b][c, c, c, ..., c]
       ^                ^                ^</code></pre><p>Your cache loves this. On the first Execute, on an instant you access that <code>a</code> it will cause a cache miss. But due to <strong>cache line </strong>being a fixed size (e.g. 64 bytes) larger than the <code>a</code> component (<code>int</code> = 4 bytes) you want, &#xA0;cache already know the location of 16 consecutive integers as of the 1st execute. Therefore for 2nd ~ 16th Execute, reading/writing to <code>a</code> will result in a cache hit.</p><p>The same happened to <code>b</code> and <code>c</code>, it will all cache misses on the first execute then you get bonus hits on some amount of entities coming after. So often, small and modular components results in more automatic performance improvement in DOTS. The sales pitch of DOTS is that if the API is friendly enough for you to make most of your game this way, the game simply will be faster.</p><p>But even if you must use large and complex components, even larger than cache line and having a mix of complex sized things like <code>bool</code> in-between, at that point it might get hard to continue picturing the cache while you code. I think you don&apos;t have to worry if that is the case, you would still randomly get better performance overall depending on your logic and which fields you access on that large component. There are a lot of effort in Entities that try to align your <code>struct</code> in favor of the cache. </p><h2 id="points-to">Points to?</h2><p>Both style of entity iteration looked a bit different, but you can guess the reason that they are trying to accommodate the cache.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public partial struct MyJob : IJobEntity
{
    public void Execute(in ComponentA a, ref ComponentB b, in ComponentC c)
    {
        b.Data += a.Data + c.Data;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>In <code>IJobEntity</code> style, the design is such that you must either use <code>in</code> (basically <code>readonly ref</code>) &#xA0;or <code>ref</code> for RO and RW access. Then you read or write these variable normally. The keyword could pass <code>struct</code> by reference instead of by value.</p><p>If they are passed by value, the code somewhere (the source generator part) that call this <code>Execute</code> would already cause cache hit / miss way before your code. And that might be bad when you want many kind of components coming in ready for use, but has conditional access and you might ended up not using some of them. It&apos;d be a waste of cache if they are already accessed. (Though even in that case, the cache has a built-in policy that least often used one will quickly get kicked out, etc.) With <code>in</code> and <code>ref</code>, it is only after you touch (read / write) these incoming variables that you do something to the cache and get some bonus hits for next few entities. </p><!--kg-card-begin: markdown--><pre><code class="language-csharp">foreach (
    var (a, b, c) in
    SystemAPI.Query&lt;RefRO&lt;ComponentA&gt;, RefRW&lt;ComponentB&gt;, RefRO&lt;ComponentC&gt;&gt;()
)
{
    b.ValueRW.Data += a.ValueRO.Data + c.ValueRO.Data;
}
</code></pre>
<!--kg-card-end: markdown--><p>In idiomatic <code>foreach</code> style, the way they want you to write C# doesn&apos;t permit adding <code>ref</code> or <code>in</code> there. So the trick is that <code>RefRO</code> and <code>RefRW</code> type using C# property (<code>ValueRW</code> / <code>ValueRO</code>) to facilitate the same &quot;cache only if you touch it in your code&quot;. You need a few more <code>.</code> to get to the data and a bit more verbose, but for performance it&apos;s a good deal.</p><h2 id="compared-with-old-gameobject-way">Compared with old GameObject way</h2><p>Disclaimer that I don&apos;t actually know how <code>GameObject</code> scattered on your Hierarchy ended up in memory, just a guess it might be this way. The fact that I don&apos;t know it, and I know how the memory looks like in DOTS, is already an improvement of DOTS over the old ways...</p><p>So you might want to loop through game objects <code>X</code>, <code>Y</code>, <code>Z</code> in an array, with component <code>A</code>, <code>B</code>, <code>C</code> attached on all of them. If &#xA0;you have an array of <code>GameObject</code> type, then you have an array of pointers. Reading the first element of this array and causing cache miss won&apos;t give you any exciting bonus. Supposed that if your cache line is very big, the best is you know the address of the next <code>GameObject</code>, not its data you are interested in.</p><pre><code class="language-csharp">// 1st iteration
Array of objects : [X, Y, Z]
                    ^
                    
// 2st iteration
Array of objects : [X, Y, Z]
                       ^
                       
// 3rd iteration
Array of objects : [X, Y, Z]
                          ^

Somewhere in the memory : [(X)Transform, a, b, c]??????????[(Y)Transform, a, b, c]???[(Z)Transform, a, b, c]?????</code></pre><p>Then you might have to get each component individually and cause an another jump. If memory is really looking this way, with mandatory <code>Transform</code> component or other custom components you are not interested in right now, with <code>????</code> being unknown length, and before DOTS you don&apos;t care since you are going pointer-happy and jumps repeatedly without caring the cache, the cache line will rarely get good bonus data that is useful in the next iteration loop. The <code>a</code> data of each <code>GameObject</code> are so far away from each other.</p><h2 id="burst-and-simd-automatic-vectorization">Burst and SIMD : Automatic vectorization</h2><p>The cache line bonus thing of reading the first <code>int</code> and knowing 15 more <code>int</code> can be further improved. What requested data from cache is not your C# code but a compiled assembly code. C# code might looks already cache-friendly, but choosing the right assembly code can push the limit of cache-friendliness further.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public partial struct MyJob : IJobEntity
{
    public void Execute(in ComponentA a, ref ComponentB b, in ComponentC c)
    {
        b.Data += a.Data + c.Data;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Look at this work again. I mentioned that on the first read of <code>a</code>, you would automatically get future <code>a</code> into the cache for the next loop. But before you get to the next loop, you must also read <code>c</code>, read <code>b</code>, then write to <code>b</code>, to get to the bonus <code>a</code> waiting for you.</p><p>But how about using all those future <code>a</code> <strong>in one loop, </strong>the same for <code>b</code> and <code>c</code> too?</p><p>If you mark the job for Burst compilation, it can look at anything looping (&quot;scalar code&quot;, which in DOTS is common, and looked natural for the programmer) and rewrite them such that it performs the work of many consecutive loops in one go, becoming a &quot;vector code&quot;.</p><p>It is a good job for machine to do for you since manually writing vector code often mask your actual intent with a lot of optimizations. Imagine if you are writing such that it perform 4 loops worth of work in one. Not only you need to pack up the variables and somehow make sure it became the right vector assembly command (depending on the target machine&apos;s processor), if the last loop ended up not adding up to 4 you must do the remaining in scalar way. Imagine doing that by hand on every loop in the game?</p><p>Let&apos;s slap on <code>[BurstCompile]</code> right now and see how smart it is. I&apos;ll add a constant <code>1234</code> into the mix just so it is obvious in the <code>AVX2</code> assembly in the Burst Inspector. I like selecting <code>AVX2</code> for performance debugging because <a href="https://en.wikipedia.org/wiki/Advanced_Vector_Extensions">it introduces 256 bits commands</a>. It is obvious when Burst is trying to take advantage of them.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">[BurstCompile]
public partial struct MyJob : IJobEntity
{
    public void Execute(in ComponentA a, ref ComponentB b, in ComponentC c)
    {
        b.Data += a.Data + c.Data + 1234;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>The Burst Inspector comes with useful debug mode (&quot;Coloured With Full Debug Information&quot;) that makes it easy to spot if it is doing the expected tricks. Here, you can see the comment saying about making an 8 element array of repeated <code>1234</code> (that means this one <code>ymm</code> register fits 8 <code>int</code>, 32 * 8 = 256 bits register), which is not exactly what you wrote, but it is clearly cooking. Pink instructions indicates vector assembly command, they starts with <code>v</code>. In other words, pink is good.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-28.png" class="kg-image" alt="Thinking in Cache" loading="lazy" width="2000" height="930" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-28.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-28.png 1000w, https://gametorrahod.com/content/images/size/w1600/2024/05/image-28.png 1600w, https://gametorrahod.com/content/images/size/w2400/2024/05/image-28.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>And then you see the effort of utilizing this chunky <code>ymm_</code> register.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-29.png" class="kg-image" alt="Thinking in Cache" loading="lazy" width="1796" height="1216" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-29.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-29.png 1000w, https://gametorrahod.com/content/images/size/w1600/2024/05/image-29.png 1600w, https://gametorrahod.com/content/images/2024/05/image-29.png 1796w" sizes="(min-width: 720px) 720px"></figure><p>Interestingly, it tries to go beyond and even double up each command, using both <code>ymm1</code> and <code>ymm2</code> as its workbench at the same time. So this is like working on 16 <code>int</code> consecutively, even though one vector command supports only up to 8 <code>int</code>. I could see this double up of work might be to take advantage of bigger cache line. (The <code>+32</code> bytes offset is the size of 8 <code>int</code>.)</p><p>After reading out 8 + 8 <code>int</code> from the native array of <code>B</code> (<code>r14</code>, you can see on <code>DEBUG_VALUE</code> too), adding <code>rax</code> and <code>rbx</code> is adding multiple integers from <code>A</code> and <code>C</code> respectively. The preparation of <code>ymm0</code> (eight <code>1234</code>) earlier is then &#xA0;added to everything.</p><p>The following <code>add</code> <code>cmp</code> <code>jne</code> is that leftover loop thing I talked about. If the remaining works are less than 16 <code>int</code> (the <code>64</code> you see is the size of 16 <code>int</code>) , then...</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-30.png" class="kg-image" alt="Thinking in Cache" loading="lazy" width="2000" height="1072" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-30.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-30.png 1000w, https://gametorrahod.com/content/images/size/w1600/2024/05/image-30.png 1600w, https://gametorrahod.com/content/images/2024/05/image-30.png 2064w" sizes="(min-width: 720px) 720px"></figure><p>It will finish up the rest one by one without vectorization. <code>rsi</code> moves forward by 4 bytes each time so that&apos;s surely one <code>int</code> each, and you see that <code>r14</code>, <code>rax</code>, <code>rbx</code> casts are the same, and topped up with <code>1234</code>.</p><p>This is wonderful that you get all these insane code (both the vectorized part and scalar part) without writing them! </p>]]></content:encoded></item><item><title><![CDATA[Chunk's Change Version]]></title><description><![CDATA[DOTS comes with a per-chunk versioning and a related filter feature. This feature can optimize processing the data only when it mattered.]]></description><link>https://gametorrahod.com/change-version/</link><guid isPermaLink="false">6641b9a9981d500594734ef4</guid><category><![CDATA[Unity DOTS]]></category><dc:creator><![CDATA[Sirawat Pitaksarit / 5argon]]></dc:creator><pubDate>Mon, 13 May 2024 17:05:42 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1715415272375-1d0c8ef29b5b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8YWxsfDIwfHx8fHx8Mnx8MTcxNTYxODE4NXw&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1715415272375-1d0c8ef29b5b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8YWxsfDIwfHx8fHx8Mnx8MTcxNTYxODE4NXw&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Chunk&apos;s Change Version"><p>DOTS comes with a per-chunk versioning and a related filter feature. This feature can optimize processing the data only when it mattered.</p><p>This article highlights where they are in the API and their behaviour / quirks.</p><h2 id="utilizing-the-change-version">Utilizing the change version</h2><h3 id="using-a-filter">Using a filter</h3><p>The query way provides an easy to use filter system.</p><ul><li>On <code>SystemAPI.Query</code> idiomatic <code>foreach</code> : &#xA0;<code>QueryEnumerable&lt;T1&gt;</code> has <code>.WithChangeFilter</code> you can use, with <code>&lt;T&gt;</code> and <code>&lt;T1, T2&gt;</code>. So you can&apos;t add more than 2 components to check changed status at the same time.</li><li>For <code>SystemAPI.QueryBuilder()</code>, you must <code>.Build</code> it first, then there is <code>.AddChangedVersionFilter</code> you can use. This call is additive, but inside the source code, there is still a hard-coded limit of 2 components to filter on change version. (i.e. Error if you <code>.AddChangedVersionFilter</code> 3 times.)</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-13.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="1992" height="566" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-13.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-13.png 1000w, https://gametorrahod.com/content/images/size/w1600/2024/05/image-13.png 1600w, https://gametorrahod.com/content/images/2024/05/image-13.png 1992w" sizes="(min-width: 720px) 720px"></figure><p>Both ways you can have max 2 components to check, so this is an important number to keep in mind when designing your logic. Also take note that the noun is either &quot;change filter&quot; or &quot;changed version filter&quot; (with the -d). But the thing that is stored per-chunk to make this feature work is called &quot;change version&quot; number (without the -d), explained in the incoming section.</p><p>The query has <code>.IsEmpty</code> which account for the filter, unlike <code>.IsEmptyIgnoreFilter</code> version. In Unity&apos;s code there are many early out optimization with <code>.IsEmptyIgnoreFilter</code> in main thread code. You maybe able to do similar thing too.</p><h3 id="manual-checking-on-chunk-iteration">Manual checking on chunk iteration</h3><p>However if you decided to get more involved with per-chunk iteration (<code>IJobChunk</code>) there is no longer a convenient filter to help you. You can use <code>.DidChange</code> available on <code>ArchetypeChunk</code> that is given you per iteration. By more involved, you now have to go through the trouble of bringing in these into the job : </p><ul><li>The system&apos;s <code>uint</code> last version number : Obtained from <code>SystemState.LastSystemVersion</code>. &quot;Last&quot; meant the last time (not this time) that the system updates, what <strong>was</strong> its version? </li><li>The <code>ComponentTypeHandle&lt;T&gt;</code> : Obtained from <code>SystemAPI.GetComponentTypeHandle(isReadOnly: true)</code>. Note that they designed it like this so that there are hoops you must jump through in order for the system to be notified of type&apos;s read dependency on the job it is going to schedule / run.</li></ul><!--kg-card-begin: markdown--><pre><code class="language-csharp">public struct ChunkJobWithChangeFilter : IJobChunk
{
    public ComponentTypeHandle&lt;SampleDataComponent&gt; SampleDataComponentHandle;
    public uint LastSystemVersion;

    public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
        in v128 chunkEnabledMask)
    {
        if (chunk.DidChange(ref SampleDataComponentHandle, LastSystemVersion))
        {
            // Do something
        }
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>This chunk-based way you are no longer bound by 2 components limit, and also can <code>if</code> <code>else</code> as complex as you wish. (e.g. If A component chunk is marked as changed, do this. If B component chunk is marked as unchanged, do that instead.)</p><p>This chunk-based way also reveals the exact mechanics how it obtains the boolean &quot;changed?&quot;, by comparing chunk&apos;s changed version number with system&apos;s.</p><h2 id="how-the-change-version-works">How the change version works</h2><p>This I think is more complicated than using the filter, and official documentation is not making it very clear. There are 3 version numbers in play, to make it easier to differentiate I&apos;m going to mark them with emoji just for this section. </p><p>There is a number called &#xA0;&#x1F30D; global system version. This version increases by 1 for each consecutive system updated, including system groups. <strong>After</strong> each system updates, the system remember this number as its &#x21A9;&#xFE0F; last system version. Therefore <strong>during </strong>the update, its &#x21A9;&#xFE0F; last system version could determine when it was updated the previous frame.</p><p>Then, there is a <strong>per-chunk</strong> number called &#x270F;&#xFE0F; change version. How the chunk is considered &quot;changed&quot; in order to update this number is rather crude : Instantly when a <strong>query </strong>is executed related to that component and it has <strong>write permission</strong>. Then, <strong>chunks</strong> returned for that query will all get their &#x270F;&#xFE0F; change version updated.</p><p>It does not check whether system actually &quot;change&quot; the component&apos;s value with its work using the query. It just see the write dependency and thought the system &quot;probably&quot; changed that for all the matched chunks.</p><p>It is per chunk. There are other entities in the chunk that are not actually changed and still get iterated. Your logic must not be destructive to these entities. Try treating changed filter as <strong>optimization</strong> rather than branching logic, always imagine if the changed filter is removed, everything must still work like before but with worse performance.</p><p>To record that the chunk has changed, its &#x270F;&#xFE0F; change verion is updated to the &#x1F30D; global system version. Note that &#x21A9;&#xFE0F; last system version is still behind &#x1F30D; global system version <strong>during</strong> the update. Only <strong>after </strong>the update that it is also catch up to &#x1F30D; global system version, making it equal to the &#x270F;&#xFE0F; change version it just recorded. </p><p>Finally we can get to how it works : If chunk&apos;s &#x270F;&#xFE0F; change version is <strong>higher</strong> than &#x21A9;&#xFE0F; last system version then it had changed. (&quot;Had changed since the last time this system updated.&quot;, to be exact.) Equal or lower meant it had not changed.</p><p>Supposed that System A writes to a component during its update, <strong>during</strong> the update, chunk&apos;s &#x270F;&#xFE0F; change version would be ahead the system&apos;s &#x21A9;&#xFE0F; last system version for a brief moment (updates to &#x1F30D; global system version). <strong>After </strong>the update, &#x21A9;&#xFE0F; last system version is then updated to be &#x1F30D; global system version to catch up too. (They are equal now.) This means if there is no other system doing anything to this component, the next frame, equal number meant it has not changed and query will not return the chunks.</p><p>But supposed that there is a System B following immediately after A and it writes to this component too and recorded a higher &#x270F;&#xFE0F; change version into the chunk, in the next frame System A would be able to detect the change made by System B the last frame.</p><h2 id="tests">Tests</h2><p>To reinforce the learning, here are some tests. Before the tests, I&apos;m introducting 2 actors which is a simple component data (that we are going to change, and check its change version) and a big internal capacity dynamic buffer type designed to bully the archetype to have many chunks, so we could see clearly when change version works on one chunk and not the others.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public struct SampleDataComponent : IComponentData
{
    public int Data;
}

[InternalBufferCapacity(256)]
public struct FatBuffer : IBufferElementData
{
    public int BufferData;
}
</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2024/05/image-15.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="500" height="426"><figcaption>It could only fit 15 entities per chunk, so if I create 90 entities of this archetype, I would have 6 chunks.</figcaption></figure><h3 id="last-system-version-starts-out-at-0-a-special-number">Last system version starts out at 0, a special number</h3><p>The first time the system updates, there is no &quot;last&quot; to look back to, therefore it is 0. Everything is considered &quot;changed&quot;. This further emphasis that this feature should be an <strong>optimization, </strong>if the filter is somehow gone (work on everything) the result should be the same, just that the work might be a bit more redundant.</p><p>This code prints the <code>OnUpdate</code> forever with increasing frame number and system version number, but print the <code>foreach</code> exactly 90 times and never again. I name the entity on create so that on logging I could get its name.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public partial struct SystemA : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        var myArchetype = state.EntityManager.CreateArchetype(typeof(SampleDataComponent), typeof(FatBuffer));
        for (var i = 0; i &lt; 90; i++)
        {
            var createdEntity = state.EntityManager.CreateEntity(myArchetype);
            state.EntityManager.SetName(createdEntity, $&quot;Entity No. {i}&quot;);
        }
    }

    public void OnUpdate(ref SystemState state)
    {
        Debug.Log($&quot;System A : Update at frame {Time.frameCount}, last system version {state.LastSystemVersion}, current global {state.GlobalSystemVersion}&quot;);
        foreach (
            var (sampleData ,entity) in
            SystemAPI.Query&lt;RefRO&lt;SampleDataComponent&gt;&gt;()
                .WithChangeFilter&lt;SampleDataComponent&gt;()
                .WithEntityAccess()
        )
        {
            Debug.Log($&quot;System A : {state.EntityManager.GetName(entity)} foreach at frame {Time.frameCount}&quot;);
        }
    }
}
</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-24.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="1060" height="340" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-24.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-24.png 1000w, https://gametorrahod.com/content/images/2024/05/image-24.png 1060w" sizes="(min-width: 720px) 720px"></figure><h3 id="system-always-update-unless">System always update unless...</h3><p>As evidenced in the previous example, the system&apos;s <code>OnUpdate</code> does not care about whether its query get 0 match or not. So in effect this change version feature can&apos;t affect the system&apos;s workings. This behaviour matches <code>MonoBehaviour</code> one so it is easy to understand.</p><p>But you can go advanced and register the query (should be in your <code>OnCreate</code>) with the following methods on <code>SystemState</code> to make the update conditional :</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">RequireForUpdate(EntityQuery query) : void
RequireForUpdate&lt;T&gt;() : void
RequireAnyForUpdate(params EntityQuery[] queries) : void
RequireAnyForUpdate(NativeArray&lt;EntityQuery&gt; queries) : void
</code></pre>
<!--kg-card-end: markdown--><p><code>RequireForUpdate&lt;T&gt;</code> is handy for system that is going to process a singleton component.</p><p>There is also <code>[RequireMatchingQueriesForUpdate]</code> attribute to paste on top of your system and you don&apos;t even have to register anything manually. The system knows its queries and this attribute will add them all to the requirement.</p><p>You would imagine pasting this attribute to the previous example will only prints 91 times and stop, but that&apos;s not the case (lol!) as both <code>RequireForUpdate</code> and <code>[RequireMatchingQueriesForUpdate]</code> ignores all the filters in the query. (3 kinds : Change version filters, shared component filters, order version filters.) Anyway, preventing the system&apos;s update is not our focus here. Let&apos;s focus on just whether the query with change version filter works or not.</p><h3 id="system-version-increases-globally-after-each-system">System version increases globally after each system</h3><p>The previous example, if you look at frame 2 and beyond, you might be surprised at the system version numbers.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-25.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="1072" height="470" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-25.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-25.png 1000w, https://gametorrahod.com/content/images/2024/05/image-25.png 1072w" sizes="(min-width: 720px) 720px"></figure><p>As mentioned, global system version (and therefore also last system version) is also increasing when other systems updates. (There are many built-in ones that came with the package. There are 54 more systems along the way.</p><p>Now it is time to introduce a similar <code>SystemB</code> with <code>[UpdateAfter(typeof(SystemA))]</code>, it would ended up like this.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public partial struct SystemA : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        var myArchetype = state.EntityManager.CreateArchetype(typeof(SampleDataComponent), typeof(FatBuffer));
        for (var i = 0; i &lt; 90; i++)
        {
            var createdEntity = state.EntityManager.CreateEntity(myArchetype);
            state.EntityManager.SetName(createdEntity, $&quot;Entity No. {i}&quot;);
        }
    }

    public void OnUpdate(ref SystemState state)
    {
        Debug.Log($&quot;System A : Update at frame {Time.frameCount}, last system version {state.LastSystemVersion}, current global {state.GlobalSystemVersion}&quot;);
        foreach (
            var (sampleData ,entity) in
            SystemAPI.Query&lt;RefRO&lt;SampleDataComponent&gt;&gt;()
                .WithChangeFilter&lt;SampleDataComponent&gt;()
                .WithEntityAccess()
        )
        {
            Debug.Log($&quot;System A : {state.EntityManager.GetName(entity)} foreach at frame {Time.frameCount}&quot;);
        }
    }
}

[UpdateAfter(typeof(SystemA))]
public partial struct SystemB : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        Debug.Log($&quot;System B : Update at frame {Time.frameCount}, last system version {state.LastSystemVersion}, current global {state.GlobalSystemVersion}&quot;);
        foreach (
            var (sampleData ,entity) in
            SystemAPI.Query&lt;RefRO&lt;SampleDataComponent&gt;&gt;()
                .WithChangeFilter&lt;SampleDataComponent&gt;()
                .WithEntityAccess()
        )
        {
            Debug.Log($&quot;System B : {state.EntityManager.GetName(entity)} foreach at frame {Time.frameCount}&quot;);
        }
    }
}

</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-26.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="1130" height="636" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-26.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-26.png 1000w, https://gametorrahod.com/content/images/2024/05/image-26.png 1130w" sizes="(min-width: 720px) 720px"></figure><p>Well, turns out B is not updating immediately after A but rather there are 3 systems that updated between them. You can use the Systems debugger to see what came in-between. There they are.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-22.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="1610" height="174" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-22.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-22.png 1000w, https://gametorrahod.com/content/images/size/w1600/2024/05/image-22.png 1600w, https://gametorrahod.com/content/images/2024/05/image-22.png 1610w" sizes="(min-width: 720px) 720px"></figure><h3 id="rw-access-is-enough-to-update-the-change-version">RW access is enough to update the change version</h3><p>If I first just remove <code>.WithChangeFilter()</code> from <code>SystemA</code> (retains the RO), I would get <code>SystemA</code> that unconditionally <code>foreach</code> on every frames 90 times per frame, and <code>SystemB</code> which still has <code>.WithChangeFilter()</code> won&apos;t update other than the first time, since RO doesn&apos;t flag the chunk to get a new change version.</p><p>And then I change RO in <code>SystemA</code> to RW, now <code>.WithChangeFilter()</code> on <code>SystemB</code> (still RO) see the change every time since it always run after <code>SystemA</code>, and ended up updating as well on every frames. We can clearly see that write dependency is what caused the change because the logic is just reading and logging.</p><p>The query needs to occur for RW to update all the matched chunk&apos;s change version. If I put an unlikely <code>if</code> before the <code>foreach</code>, then <code>SystemB</code> will not be notified of the change even though <code>SystemA</code> has RW permission of that component and its <code>OnUpdate</code> occurs. </p><h3 id="change-version-update-is-per-chunk">Change version update is per chunk</h3><p>Remember that I intentionally make the archetype big enough that there could be just 15 entities per chunk. To test this out, I&apos;ll just add a <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.2/manual/components-tag.html">tag component</a> <code>TagForChunk</code> to the last 16 entities when I create 90 entities in <code>SystemA</code>&apos;s <code>OnCreate</code>. The result should be that we have 2 chunks of this new archetype that includes the tag. One chunk with 15 entities, another with 1 entity.</p><p>Then modify the &quot;work&quot; in <code>SystemA</code> to have <code>RefRO&lt;TagForChunk&gt;</code>. The work stays the same (logging and not actually writing), so this work should update change version of those 2 chunks (because there is still <code>RefRW</code> on the <code>SampleDataComponent</code>) .</p><p>And important that in <code>SystemB</code>, everything stays the same including <strong>not </strong>caring about <code>TagForChunk</code> at all.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public struct TagForChunk : IComponentData
{
}

public partial struct SystemA : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        var myArchetype = state.EntityManager.CreateArchetype(typeof(SampleDataComponent), typeof(FatBuffer));
        var archetypeWithTag =
            state.EntityManager.CreateArchetype(typeof(SampleDataComponent), typeof(FatBuffer), typeof(TagForChunk));
        for (var i = 0; i &lt; 90; i++)
        {
            Entity createdEntity = state.EntityManager.CreateEntity(i &gt;= 74 ? archetypeWithTag : myArchetype);
            state.EntityManager.SetName(createdEntity, $&quot;Entity No. {i}&quot;);
        }
    }

    public void OnUpdate(ref SystemState state)
    {
        Debug.Log(
            $&quot;System A : Update at frame {Time.frameCount}, last system version {state.LastSystemVersion}, current global {state.GlobalSystemVersion}&quot;);
        foreach (
            var (sampleData, tag, entity) in
            SystemAPI.Query&lt;RefRW&lt;SampleDataComponent&gt;, RefRO&lt;TagForChunk&gt;&gt;()
                .WithEntityAccess()
        )
        {
            Debug.Log($&quot;System A : {state.EntityManager.GetName(entity)} foreach at frame {Time.frameCount}&quot;);
        }
    }
}

[UpdateAfter(typeof(SystemA))]
public partial struct SystemB : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        Debug.Log(
            $&quot;System B : Update at frame {Time.frameCount}, last system version {state.LastSystemVersion}, current global {state.GlobalSystemVersion}&quot;);
        foreach (
            var (sampleData, entity) in
            SystemAPI.Query&lt;RefRO&lt;SampleDataComponent&gt;&gt;()
                .WithChangeFilter&lt;SampleDataComponent&gt;()
                .WithEntityAccess()
        )
        {
            Debug.Log($&quot;System B : {state.EntityManager.GetName(entity)} foreach at frame {Time.frameCount}&quot;);
        }
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>The result is it is <strong>as if</strong> <code>SystemB</code> has <code>RefRO&lt;TagForChunk&gt;</code> too, but it was all the work of <code>.WithChangeFilter&lt;SampleDataComponent&gt;</code> that results from <code>SystemA</code> working according to the tag. The update starts from Entity No. 74 to Entity No. 90. (15 + 1 entities across 2 chunks.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-27.png" class="kg-image" alt="Chunk&apos;s Change Version" loading="lazy" width="1020" height="290" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-27.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-27.png 1000w, https://gametorrahod.com/content/images/2024/05/image-27.png 1020w" sizes="(min-width: 720px) 720px"></figure><h3 id="enableable-components-does-not-work-on-chunk-level">Enableable components does not work on chunk level</h3><p>Enableable component is a feature that works <strong>per entity. </strong>The source generator will query all chunks, updates the version of RW component of all chunks, then decide for each entity whether it get to work or not.</p><p>So if I change the tagging in the previous example to instead disable <code>SampleDataComponent</code> for last 16 entities, because we have 6 chunks, theoretically the final chunk could be skipped entirely. But since it doesn&apos;t work that way, all chunks ended up being changed.</p><h3 id="bonus-reminder-about-ijobchunk">Bonus reminder about IJobChunk</h3><p>When a system run / schedule <code>IJobChunk</code>, the RW or RO permission is decided on whether input <code>ComponentTypeHandle&lt;T&gt;</code> coming into the job (required to get <code>NativeArray&lt;T&gt;</code> from <code>ArchetypeChunk</code> on each per-chunk iteration) was created in the system with <code>SystemAPI.GetComponentTypeHandle&lt;T&gt;(isReadOnly: true OR false)</code>.</p><p>(There is also <code>[ReadOnly]</code> attribute you could paste on the input to the job, but I don&apos;t think it let the system know.)</p><h2 id="advanced-per-entity-change-checking">Advanced : Per-Entity Change Checking</h2><p>This coding pattern is taken from Unity&apos;s official <code>Unity.Transform</code> package, so I think we can trust it as a good pattern?</p><p>You want to do heavy work on component value&apos;s change. A per-chunk <code>DidChange</code> will work, but other entities not really changed (or worst, the chunk got RW but nothing changed) will also get the work just because an entity in the same chunk changed, only to get the computed result to be the same as they are right now, so it is a useless work.</p><p><strong>Solution : </strong>By creating a new &quot;previous&quot; component with the same fields to pair with that you want to check for a &quot;real change&quot;. (Remembering that some components have a &quot;previous pair&quot; is also a pain, so you might also have to go through trouble of writing a system to automatically add this &quot;previous&quot; component whenever it found a real one.) &#xA0;You can then put a value comparision check just behind the <code>DidChange</code>&apos;s initial rough filter so you can check per-entity if it really changed. Then you update previous to be the present one as well if it ended up changed.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">if (chunk.DidChange(ref ParentTypeHandle, LastSystemVersion) ||
    chunk.DidChange(ref PreviousParentTypeHandle, LastSystemVersion))
{
    var chunkPreviousParents = chunk.GetNativeArray(ref PreviousParentTypeHandle);
    var chunkParents = chunk.GetNativeArray(ref ParentTypeHandle);
    var chunkEntities = chunk.GetNativeArray(EntityTypeHandle);

    for (int j = 0, chunkEntityCount = chunk.Count; j &lt; chunkEntityCount; j++)
    {
        if (chunkParents[j].Value != chunkPreviousParents[j].Value)
        {
            // Work...
        }
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Now for details if you are still interested... otherwise you can end this article here.</p><p>In the ECS transforms system, you build a transform hierarchy <strong>bottom-up</strong> by attaching <code>Parent</code> to an entity and let it say who the parent is. This make it easy to move an entity (along with an entire tree of its children right now) under some other entity by setting just a single entity&apos;s <code>Parent</code> component to point to a new one. No need to change any of its children.</p><p>But programmer often wants to iterate through children, and knowing just <code>Parent</code> is not so useful. So there is a system that would keep looking at all <code>Parent</code> in the world and figure out the hierarchy tree and maintain the buffer component <code>Child</code> for each entity. As you can imagine, this work is looking to be intensive and must be performant.</p><p>The system that do so cannot just work on any new <code>Parent</code> it found and say &quot;Yes, I have already worked on this one&quot; (and perhaps slap a tag component on it), it must additionally : </p><ol><li>Always actively look for field change inside any <code>Parent</code> component.</li><li>If the component <code>Parent</code> is removed <strong>or</strong> the entity with <code>Parent</code> is destroyed, then the affected parent will need their buffer updated.</li></ol><p>So how they did it that solve both points at the same time, is to have a &quot;previous&quot; component named <code>PreviousParent</code> with the same field as <code>Parent</code> as a pair. The fact that it is an exact pair solves point 1. This component must be <code>ICleanupComponentData</code> in order to solve point 2.</p><p>This component should be automatically added when system detects a new <code>Parent</code> (<code>WithAll&lt;Parent&gt;().WithNone&lt;PreviousParent&gt;()</code>) coming into the world. At this point the previous parent starts by copying the value of the present one. Now you have got a per-entity change checking pattern working! When <code>.DidChange</code> detected a chunk-level change (or just some careless RW), we have a 2nd level check, by perform a linear iteration through native array of <code>Parent</code> and <code>PreviousParent</code> and really see for real if the value differs entity by entity.</p><p>Though unrelated with change version topic of this blog post, <code>ICleanupComponentData</code> gives the ability to react to both kinds of removal (remove just the component, or the entity got outright destroyed) with this kind of query : <code>.WithAllRW&lt;PreviousParent&gt;().WithNone&lt;Parent&gt;()</code>. When it detects such moment, <code>PreviousParent</code> serves an another purpose than &quot;true change checking&quot; that is to notify the parent of its deceased child (how sad!) so it could update its children.</p>]]></content:encoded></item><item><title><![CDATA[Enableable Components Generated Code]]></title><description><![CDATA[On surface this feature sounds like a nice little thing they added for us, but underneath it has a lot going on.]]></description><link>https://gametorrahod.com/enableable-generated-code/</link><guid isPermaLink="false">6634ab32981d5005947349c9</guid><category><![CDATA[Unity DOTS]]></category><dc:creator><![CDATA[Sirawat Pitaksarit / 5argon]]></dc:creator><pubDate>Fri, 03 May 2024 20:15:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1623949676892-0e88eea8f940?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEwfHxzd2l0Y2h8ZW58MHx8fHwxNzE0NzY2NTc5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1623949676892-0e88eea8f940?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEwfHxzd2l0Y2h8ZW58MHx8fHwxNzE0NzY2NTc5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Enableable Components Generated Code"><p>The <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.2/manual/components-enableable.html">Enableable Component</a> feature is described very succinctly as saving you small trouble of performing the common trick of sticking a <code>bool</code> in a component you want to disable without causing structural change. Also don&apos;t forget that you need to write the <code>if</code> checking that <code>bool</code> on your job code to make use of it.</p><p>On surface this feature sounds like a nice little thing they added for us, but underneath it has a lot going on. </p><p>How many <code>1234</code> do you think you would see in the Burst compiled assembly code of this simple standalone job? </p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public struct JustCounterComponent : IComponentData
{
    public int Valuez;
}

[BurstCompile]
internal partial struct AddCounterJob : IJobEntity
{
    public void Execute(ref JustCounterComponent xyz)
    {
        xyz.Valuez += 1234;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Turns out there are 8 search hits! (6 grouped together on top, 2 more on bottom) So we could guess it spawns several different paths to call <code>Execute</code>. Now I&apos;m curious, so let&apos;s find them.</p><h2 id="analyzing-the-generated-code">Analyzing the generated code</h2><p>Roslyn Source Generator is here to stay in DOTS. The assembly code of that job is generated from C# generated code <strong>combined </strong>with your code thanks to <code>partial</code>, so it make sense if we take a look at what Roslyn generated first... just the Execute part. (There are more code generated for the <code>AddCounterJob</code>, such as letting you <code>.ScheduleParallel()</code> to it.)</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">    InternalCompilerQueryAndHandleData.TypeHandle __TypeHandle;
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkIndexInQuery, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask)
    {
        var xyzArrayIntPtr = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr&lt;global::JustCounterComponent&gt;(chunk, ref __TypeHandle.__JustCounterComponent_RW_ComponentTypeHandle);
        int chunkEntityCount = chunk.Count;
        int matchingEntityCount = 0;
        
        if (!useEnabledMask)
        {
            for(int entityIndexInChunk = 0; entityIndexInChunk &lt; chunkEntityCount; ++entityIndexInChunk)
            {
                ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
                Execute(ref xyzArrayIntPtrRef);
                matchingEntityCount++;
            }
        }
        else
        {
            int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 &lt;&lt; 1)) + global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 &lt;&lt; 1)) - 1;
            bool useRanges = edgeCount &lt;= 4;
            if (useRanges)
            {
                int entityIndexInChunk = 0;
                int chunkEndIndex = 0;
                
                while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex))
                {
                    while (entityIndexInChunk &lt; chunkEndIndex)
                    {
                        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
                        Execute(ref xyzArrayIntPtrRef);
                        entityIndexInChunk++;
                        matchingEntityCount++;
                    }
                }
            }
            else
            {
                ulong mask64 = chunkEnabledMask.ULong0;
                int count = global::Unity.Mathematics.math.min(64, chunkEntityCount);
                for (int entityIndexInChunk = 0; entityIndexInChunk &lt; count; ++entityIndexInChunk)
                {
                    if ((mask64 &amp; 1) != 0)
                    {
                        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
                        Execute(ref xyzArrayIntPtrRef);
                        matchingEntityCount++;
                    }
                    mask64 &gt;&gt;= 1;
                }
                mask64 = chunkEnabledMask.ULong1;
                for (int entityIndexInChunk = 64; entityIndexInChunk &lt; chunkEntityCount; ++entityIndexInChunk)
                {
                    if ((mask64 &amp; 1) != 0)
                    {
                        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
                        Execute(ref xyzArrayIntPtrRef);
                        matchingEntityCount++;
                    }
                    mask64 &gt;&gt;= 1;
                }
            }
        }
    }
</code></pre>
<!--kg-card-end: markdown--><p>Quite a lot of the &quot;first-class support&quot; of enableable components are in the generated code rather than in Entities library! The enable bit mask came from library, but how it decides to call <code>Execute</code> for each entity or not is right here.</p><p>It make a lot of sense though, because the enable is attached to one component, the decision to perform <code>Execute</code> or not will dynamically change depending on what components you are asking on the query. You could say this feature is only possible after Unity managed to get Roslyn Source Generator working.</p><p>I have intentionally make the incoming parameter named <code>xyz</code> so you could see clearer that it is being referenced in the generated code here.</p><p>The gist of this code is that no matter you actually use enableable components or not, the generated code doesn&apos;t care and use one central <code>Execute</code> signature that accepts <code>useEnabledMask</code> and <code>chunkEnabledMask</code> (128-bit mask where <code>1</code> represent enabled entity). Therefore that first <code>if</code> to check programmatically whether you use the feature or not is mandatory.</p><h2 id="if-enableable-feature-is-not-used">If enableable feature is not used</h2><!--kg-card-begin: markdown--><pre><code class="language-csharp">if (!useEnabledMask)
{
    for(int entityIndexInChunk = 0; entityIndexInChunk &lt; chunkEntityCount; ++entityIndexInChunk)
    {
        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
        Execute(ref xyzArrayIntPtrRef);
        matchingEntityCount++;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>You can imagine <code>useEnabledMask</code> would be <code>false</code> in this case when Unity schedules this job, and use only the thin part of that <code>if</code>, because the component is just <code>IComponentData</code> without the <code>IEnableableComponent</code>.</p><p>It is a simple for loop per entity. Each entity, it calls the real <code>Execute</code> with just 1 parameter that you wrote thanks to <code>partial</code> connecting this generated code with your code. This is where assembly code would reach the number <code>1234</code>. I don&apos;t know why they keep track of <code>matchingEntityCount</code>. Maybe there is a magic assertion somewhere or it causes some good things in the assembly?</p><h2 id="if-enableable-feature-is-used">If enableable feature is used</h2><p>That <code>bool useRanges = edgeCount &lt;= 4;</code> is interesting, and if you are like me the code smells like it is challenging me to get <code>useRanges</code> to be <code>true</code> so I get to enter a nicer looking routine with nested <code>while</code> loops.</p><p>The <code>edgeCount</code> works on 64 bit + 64 bit of the 128 bit half. Each half, it XOR with itself that is shifted left by one bit, counting how many <code>1</code> in the XOR result. Effectively it is checking how often consecutive bits transitioned (the &quot;edge&quot;) between 0 and 1. If bit pattern is <code>111111110111110000</code>, the shift-and-XOR-self result will have three <code>1</code> at each transition point. <code>edgeCount</code> is 4 because it transitioned 4 times. As for &quot;range&quot;, this pattern has 2 ranges. You look at each consecutive <code>1</code>s as a range.</p><p>Only if it detects that it transitioned no more than 4 times per chunk (effectively max 3 ranges per chunk in most cases), you get to the &quot;use ranges&quot; mode. </p><h3 id="with-while-loop-use-ranges-mode">With while loop : &quot;Use ranges&quot; mode</h3><!--kg-card-begin: markdown--><pre><code class="language-csharp">int entityIndexInChunk = 0;
int chunkEndIndex = 0;

while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex))
{
    while (entityIndexInChunk &lt; chunkEndIndex)
    {
        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
        Execute(ref xyzArrayIntPtrRef);
        entityIndexInChunk++;
        matchingEntityCount++;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Basically this mode hopes that the inner <code>while</code> can work on consecutive entities that are all enabled. When it finally hits a disabled entity, it goes to outer <code>while</code> and <code>UnsafeTryGetNextEnabledBitRange</code> (a very burstable function) jumps over all the disabled entities to start the inner <code>while</code> again at the next clump of enabled entities. Each clump of enabled entities is probably what they called a &quot;range&quot;. </p><p>I can imagine now that this is the likely mode to get used if I disable just 1 or 2 entities in an entire chunk. For example if <code>1</code> in the mask meant enabled, then if I disabled just a few entities it would look like this <code>1111111011110111111111</code>, and therefore this code would have 3 &quot;ranges&quot; to work on. Note that the outer <code>while</code> is capable of jumping over many <code>0</code>, so even if I disabled a lot of entities, if they are consecutive, this mode would still get used. (e.g. <code>1111000000000011110111111</code> this is still 3 ranges.)</p><p>Remember that the point of doing data-oriented is that the data is lining up linearly per component and the cache is nice. A big long &quot;range&quot; of enabled components preserves this goodness.</p><h3 id="with-for-loop-if">With for loop + if</h3><p>If <code>edgeCount</code> prediction says ditch the range mode and just check each entity individually, it checks each bit one by one whether it is a <code>1</code> or not (enabled) and <code>Execute</code> if it is.</p><p>It needs to work two halfs : 64 bits + 64 bits, and of course stop early if chunk contains less than 64 entities, or more than 64 but less than 128 entities.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">ulong mask64 = chunkEnabledMask.ULong0;
int count = global::Unity.Mathematics.math.min(64, chunkEntityCount);
for (int entityIndexInChunk = 0; entityIndexInChunk &lt; count; ++entityIndexInChunk)
{
    if ((mask64 &amp; 1) != 0)
    {
        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
        Execute(ref xyzArrayIntPtrRef);
        matchingEntityCount++;
    }
    mask64 &gt;&gt;= 1;
}
mask64 = chunkEnabledMask.ULong1;
for (int entityIndexInChunk = 64; entityIndexInChunk &lt; chunkEntityCount; ++entityIndexInChunk)
{
    if ((mask64 &amp; 1) != 0)
    {
        ref var xyzArrayIntPtrRef = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement&lt;global::JustCounterComponent&gt;(xyzArrayIntPtr, entityIndexInChunk);
        Execute(ref xyzArrayIntPtrRef);
        matchingEntityCount++;
    }
    mask64 &gt;&gt;= 1;
}
</code></pre>
<!--kg-card-end: markdown--><h2 id="because-this-feature-is-per-entity">Because this feature is per-entity...</h2><p>The source generator of job is able to service you the <code>if</code> <code>else</code> should this entity be executed perfectly, because job also works entity by entity. Codegen could put code in the other side of <code>partial</code> before arriving at your code in <code>Execute</code>. </p><p>In <code>IJobChunk</code> though it naturally require your manual effort. It starts at archetype chunk level and it already landed in your code, where source generator can no longer perform any more hacks for you. Instead, the API design just give you a DIY kit of the mask, and helper method for you to apply on your own. See details in <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.2/manual/iterating-data-ijobchunk-implement.html#the-useenabledmask-and-chunkenabledmask-parameters">the official documentation</a>. </p><h2 id="about-write-access">About write access</h2><p>The equivalent hack of having one <code>bool</code> field in a component and then <code>if</code> on that <code>bool</code> to check if it is enabled, the trick before we have enableable component, to enable / disable the component you would need a write access on that component to edit the <code>bool</code>. Therefore any job that need to only read this component will need to wait for the one that could write.</p><p>It is the same with official enableable component. If you want to flip the enable/disable status in-job you need a write permission on that component, and it&apos;ll affect scheduling as expected.</p><p>But since the <code>bool</code> that existed in the hack is now hidden and built-in as a 128-bit mask instead (and they provided efficient code that would flip one bit in this long mask as good as you flipping one <code>bool</code> yourself), you can no longer just add that component as a part of query and just <code>enabledFlag = false/true</code>. </p><p>If main thread, you can use <code>EntityManager</code>, or use <code>EnabledRefRO&lt;T&gt;</code> and <code>EnabledRefRW&lt;T&gt;</code> as a part of <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.2/manual/systems-systemapi-query.html">idiomatic <code>foreach</code> (<code>SystemAPI.Query</code>)</a>.</p><p>The dedicated method to do that <strong>from job</strong> is available in <code>ComponentLookup&lt;T&gt;</code>. And passing the lookup into the job will properly add write dependency. Here are relevant ones.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">IsComponentEnabled(Entity entity) : bool
IsComponentEnabled(SystemHandle systemHandle) : bool
SetComponentEnabled(SystemHandle systemHandle, bool value) : void
SetComponentEnabled(Entity entity, bool value) : void
GetEnabledRefRW&lt;T2&gt;(Entity entity) : EnabledRefRW&lt;T2&gt;
GetComponentEnabledRefRWOptional&lt;T2&gt;(Entity entity) : EnabledRefRW&lt;T2&gt;
GetEnabledRefRO&lt;T2&gt;(Entity entity) : EnabledRefRO&lt;T2&gt;
GetComponentEnabledRefROOptional&lt;T2&gt;(Entity entity) : EnabledRefRO&lt;T2&gt;
</code></pre>
<!--kg-card-end: markdown--><p><code>EntityCommandBuffer</code> and its parallel version can also work on enabling / disabling the component from job, but scheduled for later.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">// Standard
SetComponentEnabled&lt;T&gt;(Entity e, bool value) : void
SetComponentEnabled(Entity e, ComponentType componentType, bool value) : void

// ParallelWriter
SetComponentEnabled&lt;T&gt;(int sortKey, Entity e, bool value) : void
SetComponentEnabled(int sortKey, Entity e, ComponentType componentType, bool value) : void
</code></pre>
<!--kg-card-end: markdown--><p>Lastly if your job is iterating by <code>ArchetypeChunk</code>, there are extensive functions to work with enabled status. Normal ones that takes in entity index in chunk to flip one bit. Advanced ones that enable/disable the entire chunk (ones with <code>ForAll</code> suffix), or even get just the bits and mask which I can&apos;t even think of real use case of.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">IsComponentEnabled&lt;T&gt;(ref ComponentTypeHandle&lt;T&gt; typeHandle, int entityIndexInChunk) : bool
IsComponentEnabled&lt;T&gt;(ref BufferTypeHandle&lt;T&gt; bufferTypeHandle, int entityIndexInChunk) : bool
IsComponentEnabled(ref DynamicComponentTypeHandle typeHandle, int entityIndexInChunk) : bool

SetComponentEnabled&lt;T&gt;(ref ComponentTypeHandle&lt;T&gt; typeHandle, int entityIndexInChunk, bool value) : void
SetComponentEnabled&lt;T&gt;(ref BufferTypeHandle&lt;T&gt; bufferTypeHandle, int entityIndexInChunk, bool value) : void
SetComponentEnabled(ref DynamicComponentTypeHandle typeHandle, int entityIndexInChunk, bool value) : void

SetComponentEnabledForAll&lt;T&gt;(ref ComponentTypeHandle&lt;T&gt; typeHandle, bool value) : void
SetComponentEnabledForAll&lt;T&gt;(ref BufferTypeHandle&lt;T&gt; bufferTypeHandle, bool value) : void
SetComponentEnabledForAll(ref DynamicComponentTypeHandle typeHandle, bool value) : void

GetEnableableBits(ref DynamicComponentTypeHandle handle) : v128
GetEnabledMask&lt;T&gt;(ref ComponentTypeHandle&lt;T&gt; typeHandle) : EnabledMask
</code></pre>
<!--kg-card-end: markdown--><h2 id="a-limit-of-128-entities-per-chunk">A limit of 128 entities per chunk</h2><p>That mask having &quot;chunk&quot; in the name being 128 bits meant that we could only flag up to 128 entities as enabled or disabled per chunk.</p><p>The internal <code>ChunkIterationUtility.cs</code> has <code>GetEnabledMask</code> that creates this mask for one component of a chunk we could take a look. (And wow, a lot of care for creating just this 128 bit mask fast!) &#xA0;And sure enough there are hard-coded <code>128</code> around here. If you follow the code, you would eventually reach <code>TypeManager.cs</code> explicitly defining <code>public const int <strong>MaximumChunkCapacity </strong>= 128;</code> so I think that&apos;s it. Chunk right now has 2 hard limits : 16 KiB size and max 128 entities.</p><p>You can see this fact in the Archetypes debugger. An archetype of just 16 B each should be able to fit 1000 entities per chunk before the advent of enableable and the mask feature. Now it is capped to 128 entities. You could also say an archetype has &quot;optimal size&quot; of ~125 B where you get your full worth of 128 entities limit.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2024/05/image-12.png" class="kg-image" alt="Enableable Components Generated Code" loading="lazy" width="1884" height="470" srcset="https://gametorrahod.com/content/images/size/w600/2024/05/image-12.png 600w, https://gametorrahod.com/content/images/size/w1000/2024/05/image-12.png 1000w, https://gametorrahod.com/content/images/size/w1600/2024/05/image-12.png 1600w, https://gametorrahod.com/content/images/2024/05/image-12.png 1884w" sizes="(min-width: 720px) 720px"></figure><p>Is this worrying? (You might fear that for every 128 entities of single component data iteration, cache would need to jump to the next chunk&apos;s native array, instead of once every ~1000 entities) I think no. I have code some simple application with DOTS and with many kind of components added up together, it could approach that 125 B number fast. I think they have sufficiently looked at actual DOTS application for average archetype size before deciding on this 128 bit mask thing.</p><h2 id="but-you-should-not-optimize-for-enableable">But you should not optimize for enableable</h2><p>We now know if you disable just a few entities here and there (to be precise, disabled 4 or less entities in each chunk) OR you can disable a lot of consecutive entities in a chunk, it would fall into a good pattern with consecutive data while iterating.</p><p>But having to keep in mind which chunk you are working on and whether you have hit the 4 quota or not (more than 4 if you use more than 1 chunk for that archetype, now you don&apos;t even know what is the quota!), or whether this entity is next to that entity or not, I think is not worth thinking about. Technical details of deleting an entity in the middle of chunk will also just pick the last one to fill the hole, if that entity is currently disabled, you will create an &quot;edge&quot; in the mask as well.</p><p>Unity refined Entities API to let you focus on what mattered already, better not to bring those back. Just enable / disable stuff at will and be happy that it is likely very fast and all burstable. And remember that the point of enableable components are that the enable / disable patterns could be chaotic and frequent, otherwise you would rather slap on a tag component to kick it to a different archetype with a structural change.</p><p>(In the end, the benefit of this article is just so I&apos;m no longer curious so thats good!)</p>]]></content:encoded></item><item><title><![CDATA[Dear Unity DOTS Readers]]></title><description><![CDATA[I'm coming back to catch up with current version of DOTS. And I'm going to write on this blog as my tool for self learning like I used to do years ago.]]></description><link>https://gametorrahod.com/dear-unity-dots-readers/</link><guid isPermaLink="false">66262829981d5005947347bc</guid><category><![CDATA[Unity DOTS]]></category><dc:creator><![CDATA[Sirawat Pitaksarit / 5argon]]></dc:creator><pubDate>Mon, 22 Apr 2024 09:43:28 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1535813381253-2917d694ff3f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGRvdHN8ZW58MHx8fHwxNzEzNzc4OTIxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1535813381253-2917d694ff3f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGRvdHN8ZW58MHx8fHwxNzEzNzc4OTIxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Dear Unity DOTS Readers"><p>Hello, it&apos;s been quite some time since the last update. Because there is no comment box in this blog, I&apos;m not sure if there is an audience I&apos;m saying &quot;hello&quot; to right now. This blog was generic Unity blog until I got into Data-Oriented Technology Stack (DOTS) when it just came out many years ago with very wacky API compared to now, then it got substantially high amount of DOTS article, that I thought maybe there are now someone out there who remembers this blog as &quot;that khaki DOTS blog&quot; or something along that line.</p><p>While I was away, pandemic came and go, Unity had leadership drama, DOTS reached release status, and AI coding assistant has becoming pretty useful, went through 3 generations of cats, all my friends get married and have kids... it had been that long.</p><p>Before diving into what I&apos;ve been up to lately this is the TLDR : <strong>I&apos;m coming back to catch up with current version of DOTS. And I&apos;m going to write on this blog as my tool for self learning like I used to do years ago</strong>. When I type things along the way I found I could learn faster, then I got to left some digested knowledge for someone else too.</p><p>I&apos;m also looking to <strong>delete DOTS articles containing deprecated stuff</strong>, which I suspect would be over 80% of them. I&apos;m going to make this blog clean and &quot;safe&quot; for future generations of DOTS programmers to not see any unpleasant things that Unity DOTS team (thanks for your hard work...) &apos;trialed&apos; and found out ther are the &apos;error&apos;. Any search hit into this blog I want them to be immediately trustable for Entities 1.0+. So I don&apos;t want to mention what got replaced by what here because I don&apos;t want those search hits. If you used previous Entities version, <a href="https://docs.unity3d.com/Packages/com.unity.entities@1.2/manual/upgrade-guide.html#update-system-state-components-to-cleanup-components">this page</a> is excellent to learn what&apos;s going away and is replaced by what.</p><p>This process would take quite a lot of time, maybe I&apos;d rather delete everything with DOTS tag and write all of them from scratch.</p><p>For typical readers you can look forward to some new DOTS articles coming out as I re-learn it. Next up is just some &apos;5argon lore&apos; which I guess you could read to waste some time.</p><h2 id="more-about-myself-along-those-missing-years">More about myself along those missing years</h2><p>Learning DOTS while API rapidly fluctuating and keep destroying my project was tiring but I did not get tired of DOTS and quit, which some readers might assumed. I&apos;m entertained by its absurd technicality and what it wanted to achieve by doing so at all costs. No matter what Unity as a company and management went through, I always wanted DOTS skillset in my tool belt.</p><p>When it was early pandemic I simply got a full stack web dev job in financial business (generally pays well, especially compared to &apos;free&apos; DOTS research + personal little passion game I was doing) and job is extremely hard to find at that time so it is stupid to let that go and say you can&apos;t buy me out of passion with money. It is also a remote job with minimal on-site requirement, and I get to learn TypeScript, React, Go, Next.. the typical meta workhorse set to make a webapp and make money.</p><p>In fact you can if it is enough money (haha) and in no time I was able to go wander in Villa Market, a place full of imported goods, and was able to buy some mid shelf alcohol &#xA0;like Campari and Beefeater without heavily pondering about my life savings. I was able to iterate on my baking and cooking repeatedly wasting ingredients on failures after failures without worry. I could always grab any bakery and souvenirs I passed by without any thought and most noticed that somehow I looked like a much nicer person than when I was working on my game. And I was able to purchased a robot vacuum and pressure washer for my parent&apos;s house.</p><p>I could as well use money to get more productive and therefore earning more money with them in a loop, such as able to upgrade my 2015 Macbook all the way to M2, paid subscription to Copilot, or just able to order good coffee when I work at cafe at day time. It&apos;s not so different from gameplay loop in games such as Monster Hunter or Cookie Clicker where you could climb up to harder targets and farm more valuable resources. It sounded obvious, but this approach is impossible when I was working on my games and DOTS research out of passion, you know? I just can&apos;t afford to upgrade my tool of trades along the way. Getting this job is quite literally life-changing, though it limits things I could do with my upgrades, which is to work more on this job.</p><p>Along the way I&apos;m farming money, my game surely stagnates and even got removed from Google Play Store because I forgot to check mail and press some comply button. I&apos;m always looking to eventually request to reduce work and come back to DOTS, Unity, my Asset Store assets, and my game. Coming back from exchanging money for stuff, to making stuff I could call my own. But unfortunately in early phase of that product I can&apos;t really stop. I thought it&apos;d be like 1 or 2 years before it gets less busy, but turns out it took 3~4 years. </p><p>This year and this month is finally that time I&apos;m looking for. So here I am. Thanks for reading. Cheers!</p><p>Ps. Thanks to this remote job I was able to move back from capital to a more comfy hometown with my parents there. Completely unrelated to this blog but I also got into a new <a href="https://www.fantasyflightgames.com/en/products/arkham-horror-the-card-game/">Arkham Horror LCG</a> hobby and was able to connect with my highschool friends to form a regular play group. Since I already like this game, I started a static info site <a href="http://arkham-starter.com/">http://arkham-starter.com/</a> project, a very good excuse to learn Svelte, and as some stress relief from actual work. I&apos;m looking forward to apply this Svelte knowledge to remake <a href="https://duelotters.com">my game&apos;s homepage</a> when I get to that point.</p>]]></content:encoded></item><item><title><![CDATA[Publish Asset Store UPM package on which LTS version?]]></title><description><![CDATA[As a package publisher, supporting lowest possible version is ideal for covering your user bases and therefore increase revenue. But in exchange, using version too low will results in many difficult problems.]]></description><link>https://gametorrahod.com/asset-store-upm-package-lts/</link><guid isPermaLink="false">623c5d7df445b7063911bf58</guid><category><![CDATA[Unity]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Sun, 28 Nov 2021 05:21:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1621252179027-94459d278660?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGZydXN0cmF0aW9ufGVufDB8fHx8MTYzODA3NjgyOQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1621252179027-94459d278660?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGZydXN0cmF0aW9ufGVufDB8fHx8MTYzODA3NjgyOQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Publish Asset Store UPM package on which LTS version?"><p>As a package publisher, supporting lowest possible version is ideal for covering your user bases and therefore increase revenue. But in exchange, using version too low will results in many difficult problems.</p><p>It becomes a balancing act whether you think the revenue of supporting lower version worth it vs. bumping the lowest version up.</p><p>This article will highlight them all from personal experience, having maintained <a href="https://exceed7.com/introloop/">Introloop</a>, <a href="http://exceed7.com/native-audio">Native Audio</a>, and <a href="http://exceed7.com/tiny-ambience">Tiny Ambience</a>. Introloop especially, published its first version in Unity 5. (Now that takes me back! It really begins in Unity 4 or something while I was working on a game called So Many Me with an another company.)</p><h2 id="tldr">TLDR</h2><p>Develop all your UPM Asset Store packages on <strong>2019.4 LTS</strong>.</p><h2 id="obligations-to-go-back-and-check">Obligations to go back and check</h2><p>When you say your package works with Unity 5, you must actually go there and check that it works. If you develop in a single project, this may mean you &quot;lives&quot; in Unity 5 forever while developing it to ensure support.</p><p>Then when you are about to release, you upgrade sequentially to 2018 LTS, 2019 LTS, 2020 LTS, and so on. Then check on each version.</p><p>Upgrade is usually a success, not so with downgrades. After sequentially checking all future versions, you don&apos;t commit the change to version control and come back to Unity 5 to develop new features. You will cry having seen better Unity in the future and then having to come back to jurassic period to work on your stuff.</p><h2 id="always-choose-one-of-lts-versions">Always choose one of LTS versions</h2><p>Back then Unity doesn&apos;t have the LTS version that guarantee bug fixes for several years. Now that Unity has it, game developers (especially big companies) are very likely to choose these LTS versions for their game instead of any mid-year versions.</p><p>Now it is obvious the lowest support should be one of these LTS versions to support as many big company customers as possible. It will make our lives easier too when these versions would stay for a long time.</p><h2 id="choose-only-versions-in-unity-hub">Choose only versions in Unity Hub</h2><p>Also, Unity Hub is a thing now. The hub serve as an another filter that prevents average users from seeing any other obscure versions. As a package publisher, we can play along and pretend that only these versions exists to simplify development.</p><p>In this image, 2017 LTS does not exist anymore in the hub. Now your choices are just one of 2018.4 LTS, 2019.4 LTS, 2020.3 LTS.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="772" height="396" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image.png 600w, https://gametorrahod.com/content/images/2021/11/image.png 772w" sizes="(min-width: 720px) 720px"></figure><h2 id="why-supporting-2017-is-a-bad-idea">Why supporting 2017 is a bad idea</h2><p>2018.3 has a great world-shattering update that was Prefab Workflow. I remembered along with that, asset serialization changed a lot that make downgrading to 2017 impossible.</p><p>It is wise that your support starts after that &quot;new world&quot; so you don&apos;t have to deal with users that uses assets from jurassic era. (That is, just ignore 2017 and lower.)</p><p>2017 does not even have Assembly Definition asset <code>.asmdef</code>. That has a lot of features that paved way to UPM support. This also affects all your code, since <code>internal</code> doesn&apos;t work anymore when it is one big assembly. <code>[InternalsVisibleTo]</code> is unusable to create good test assembly. And you require a magic <code>Editor</code> folder! All these will make users working on 2020 LTS vomit when upgraded to that version.</p><h2 id="why-supporting-2018-is-a-bad-idea">Why supporting 2018 is a bad idea</h2><p>Whoa! 2018 still exists in Unity Hub! Why would you cut off the revenue like this?</p><p>This decision sounds familiar to dealing with <a href="https://gametorrahod.com/dealing-with-ios-android-texture-in-2019/">texture format</a>. You are worrying about users with old phones that couldn&apos;t download your game anymore if you decided to go modern. I <strong>abandoned </strong>those users and getting much nicer sleep by the way. So it is up to you!</p><p>Back to the topic. 2018 is <strong>behind </strong>one more important world-shattering update for package publisher that is <strong>UPM publishing</strong>, the next step after <code>.asmdef</code>.</p><h3 id="missing-package-manifest">Missing Package Manifest</h3><p>User now install Asset Store stuff via UPM. While that Package Manager panel will import (pour in) the asset into your <code>Assets</code> folder anyway if those packages aren&apos;t actually UPM, if your package are UPM-supported, your users will be <strong>delighted </strong>that your package stays neatly in <code>Packages</code> section instead if actually in the game.</p><p>This can be confirmed easily by going to the <a href="https://docs.unity3d.com/2019.4/Documentation/Manual/upm-manifestPkg.html">Package Manifest</a> page (the <code>package.json</code>) and click the upper left corner, the lowest LTS that this page is available is 2019.4.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-1.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1632" height="798" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-1.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-1.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-1.png 1600w, https://gametorrahod.com/content/images/2021/11/image-1.png 1632w" sizes="(min-width: 720px) 720px"></figure><p>If you could use <code>package.json</code>, it will affects : </p><ul><li>How you deliver your user an offline <strong>documentation. </strong>No more random readme file when there is a standard <a href="https://docs.unity3d.com/2020.3/Documentation/Manual/cus-document.html">where</a> to put your stuff.</li><li>How to deliver your user <strong>samples. </strong>Problem was that samples are imported into the game along with the package, then user have to remove them because they adds up to import time if they contains scripts, images, or audio. (Even when not actually used in game.) UPM comes with sample importing button with a <a href="https://docs.unity3d.com/2020.3/Documentation/Manual/cus-samples.html">new standard how to do it</a>. This came in 2019.1.</li><li>Your <strong>online </strong>documentation will suffer from &quot;if you use 2019/2018&quot; hell because how user install, consult documentations, etc. is different from 2018. You need to keep saying this to accommodate all users.</li></ul><h3 id="missing-dependencies">Missing Dependencies</h3><p>Dependencies can be automatically installed via <code>dependencies</code> in the manifest, so they don&apos;t appear as <em>game</em> dependencies.</p><p>For example if my package needs Addressables, but my user&apos;s game didn&apos;t need it, my user&apos;s game will ended up having Addressables as a result of installing my package, but Addressables aren&apos;t listed on the game.</p><p>Removing my package will instantly also remove Addressables from the game.</p><p> (You can&apos;t use range syntax like <code>^</code>, only a fixed semantic version.)</p><h3 id="missing-asmdef-features-version-defines">Missing ASMDEF features : Version Defines</h3><p>Even <code>asmdef</code> features are affected. The most impactful would be <strong>version defines</strong>. This make a define appear or disappear based on other resources. It can turn your package into a flex god, offering more extra features as user installed other Unity packages!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-2.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="878" height="284" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-2.png 600w, https://gametorrahod.com/content/images/2021/11/image-2.png 878w" sizes="(min-width: 720px) 720px"></figure><p>Bottom line : Let&apos;s together skip 2018 LTS revenue! Time will heal everything and not too long to wait, this version will fade away.</p><h2 id="what-about-2019-4-lts-then">What about 2019.4 LTS then?</h2><h3 id="c-8-0-is-not-available">C# 8.0 is not available</h3><p>This might be small, but my favorite is <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression">switch expression</a>. It reduces bugs in the code when <code>switch</code> is forced to return the same kind of values for all &quot;arms&quot; (not &quot;cases&quot;).</p><p>Another one is private <code>static</code> method. (No <code>private</code> keyword, just <code>static</code> but defined inside a method.) It completely disables variable encapsulation, making your function 100% pure. You can even use the same argument name as some variable names from an outer scope. I found that 90% of my nested functions in the method actually wants to be <code>static</code>.</p><p>If you want to maximize reach with supporting 2019.4, say goodbye to C# 8.0 and only enjoy those in your own games.</p><h3 id="missing-new-serialized-array-editor">Missing new serialized array editor</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="760" height="468" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image.png 600w, https://gametorrahod.com/content/images/2021/12/image.png 760w" sizes="(min-width: 720px) 720px"></figure><p>2020.3 came with cleaner serialized array editor drawer that has rearrange handles, along with + and - buttons!</p><p>If you are going to stay on 2019.4, it maybe better to temporarily come to 2020.3 when you are making screenshots for your documentation so your array are rendered better, if your plugin involves working with serialized array.</p><h3 id="package-manager-is-a-bit-unpolished">Package Manager is a bit unpolished</h3><p>Here is how it looks like in 2019.4 LTS :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-9.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="1217" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-9.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-9.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-9.png 1600w, https://gametorrahod.com/content/images/size/w2400/2021/11/image-9.png 2400w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-8.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="1217" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-8.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-8.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-8.png 1600w, https://gametorrahod.com/content/images/size/w2400/2021/11/image-8.png 2400w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-7.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="1324" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-7.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-7.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-7.png 1600w, https://gametorrahod.com/content/images/2021/11/image-7.png 2072w" sizes="(min-width: 720px) 720px"></figure><p>Then look at how glorious it is in 2020.3 LTS : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-4.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1948" height="1208" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-4.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-4.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-4.png 1600w, https://gametorrahod.com/content/images/2021/11/image-4.png 1948w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-5.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1948" height="910" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-5.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-5.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-5.png 1600w, https://gametorrahod.com/content/images/2021/11/image-5.png 1948w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-6.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1946" height="1210" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-6.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-6.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/11/image-6.png 1600w, https://gametorrahod.com/content/images/2021/11/image-6.png 1946w" sizes="(min-width: 720px) 720px"></figure><p>Luckily, both has samples importing buttons. So your documentation teaching user how to use samples works on either. I think differences are mostly visual.</p><p>One gotcha on the &quot;In Project&quot; viewing mode is this dropdown saying &quot;Unity Technologies&quot;.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-10.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="658" height="134" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-10.png 600w, https://gametorrahod.com/content/images/2021/11/image-10.png 658w"></figure><p>This string is pulled from <code>package.json</code> : <code>author</code> &gt; <code>name</code>. Make sure to have this consistent across all your products so they are grouped in the same drop down.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-13.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1210" height="534" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-13.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-13.png 1000w, https://gametorrahod.com/content/images/2021/11/image-13.png 1210w" sizes="(min-width: 720px) 720px"></figure><p>Initially I have packages developed on my own (&quot;5argon&quot;) for fun, and also Asset Store commercial packages I want to release as &quot;Exceed7 Experiments&quot;. Technically the name should be different like that, but I disliked how my packages are fragmented to different dropdown. Now I consistently use &quot;Exceed7 Experiments&quot; for everything, personal or commercial. (I believe <code>email</code> and <code>url</code> can vary?)</p><h3 id="the-new-publisher-portal">The new publisher portal</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-52.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1362" height="688" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-52.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-52.png 1000w, https://gametorrahod.com/content/images/2021/12/image-52.png 1362w" sizes="(min-width: 720px) 720px"></figure><p>...requires submitting <strong>new</strong> packages from 2019.4 or higher. Only updates are allowed to go as low as 2018.4.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-53.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1910" height="926" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-53.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-53.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/12/image-53.png 1600w, https://gametorrahod.com/content/images/2021/12/image-53.png 1910w" sizes="(min-width: 720px) 720px"></figure><p>And now the store itself will also follow LTS version. Store will <strong>not </strong>accept packages using LTS version that had already fallen off the support. <a href="https://forum.unity.com/threads/asset-store-following-unity-editor-lts-cycle-final-call-for-2018-x-please-update-your-assets.1014994/?_gl=1*34dwow*_gcl_aw*R0NMLjE2Mzg1OTM5MTYuQ2owS0NRaUFuYWVOQmhDVUFSSXNBQkVlZThVQ0c5LXBYWU5iVVdabC1ySWtpT1NYWTlsZy1HNDJXQlBjTEFHYllLc05LTk9HNENWZEx6SWFBaVFsRUFMd193Y0I.*_gcl_dc*R0NMLjE2Mzg1OTM5MTYuQ2owS0NRaUFuYWVOQmhDVUFSSXNBQkVlZThVQ0c5LXBYWU5iVVdabC1ySWtpT1NYWTlsZy1HNDJXQlBjTEFHYllLc05LTk9HNENWZEx6SWFBaVFsRUFMd193Y0I.&amp;_ga=2.3799097.865863714.1640504858-62677460.1628200887&amp;_gac=1.121601530.1638593916.Cj0KCQiAnaeNBhCUARIsABEee8UCG9-pXYNbUWZl-rIkiOSXY9lg-G42WBPcLAGbYKsNKNOG4CVdLzIaAiQlEALw_wcB">More info here</a>.</p><p>If you use lower than 2019 then your day is numbered!</p><h3 id="2019-4-for-life">2019.4 for life?</h3><p>Other than mentioned, personally I think <strong>2019.4 LTS </strong>is the best home for all package developers for time to come. Unity finished modernizing packages with <code>asmdef</code> and <code>package.json</code> right here, missing just some Package Manager upgrades. It will be the new Unity 5. All my packages will be staying in 2019.4 LTS for sure!</p><p>Note that due to &quot;store following the LTS&quot; policy, this would soon move to 2020.4 LTS in the next year as 2019.4 fell off.</p><h2 id="providing-extra-features-on-new-versions">Providing extra features on new versions</h2><p>You may want to commit to provide extra features on newer versions, which translated to having to maintain <code>#if</code> in your code. For example though lowest version is 2019, if 2020 is used, it gives you a bit more features but not critical to usage.</p><p>This goes beyond Unity version now that Version Defines based on package is available. Unity Timeline package <a href="https://docs.unity3d.com/Packages/com.unity.timeline@1.6/changelog/CHANGELOG.html#152---2021-01-08">version 1.5.2</a> for example, has a lot of editor functions added that it is possible to script C# to pop open a Timeline tab. I want to have a cool button that use these functions.</p><p>However, Unity 2019.4 that I decided to stay on comes with Timeline version 1.2.18 verified.</p><p>In my <code>package.json</code>, I can either : </p><ul><li>Say that it needs Timeline 1.5.2 as a dependency, then I can be free of preprocessor directives and no Version Defines.</li><li>Say nothing about Timeline dependency or explicitly states 1.2.18 which comes with Unity 2019.4, then use Version Defines to check and enable an extra define. This is then used to render that cool button in my editor code, otherwise the button is greyed out or invisible.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-17.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1358" height="384" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-17.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/11/image-17.png 1000w, https://gametorrahod.com/content/images/2021/11/image-17.png 1358w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/11/image-15.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="850" height="286" srcset="https://gametorrahod.com/content/images/size/w600/2021/11/image-15.png 600w, https://gametorrahod.com/content/images/2021/11/image-15.png 850w" sizes="(min-width: 720px) 720px"></figure><p>The preprocessors are notorious for wrecking with refactors, keep in mind. You no longer have a peace of mind using tool like Rider to refactor things as it doesn&apos;t go into deactivated section of the code. Resulting in surprise error report from users when you didn&apos;t go check on all versions which would trigger defines differently <strong>after any refactors</strong>. (i.e. Refactoring is now a potentially breaking change until you actually check with all supported versions.)</p><h2 id="publishing-gotchas">Publishing gotchas</h2><p>Here are extra tips related to Unity Asset Store Tools and the new Unity Publisher Portal.</p><h3 id="checkbox-of-doom-">Checkbox of doom (?)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-54.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1492" height="1120" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-54.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-54.png 1000w, https://gametorrahod.com/content/images/2021/12/image-54.png 1492w" sizes="(min-width: 720px) 720px"></figure><p>The 3. checkbox seems to promise compatibility with GUID reference in your <code>.asmdef</code>. But according to <a href="https://forum.unity.com/threads/asset-store-year-in-review.1217736/#post-7770552">this forum post</a>, I think it is best that you <strong>don&apos;t check this box </strong>for now, for many reasons. (lol)</p><h3 id="marketing-image-dimensions">Marketing image dimensions</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-55.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1396" height="692" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-55.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-55.png 1000w, https://gametorrahod.com/content/images/2021/12/image-55.png 1396w" sizes="(min-width: 720px) 720px"></figure><p>The new uploader does not reveal required dimension of marketing images nor how many would be needed <strong>until</strong> you drag something onto it. Therefore, I will show you the resulting dialog after dragging right now.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-56.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="688" height="540" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-56.png 600w, https://gametorrahod.com/content/images/2021/12/image-56.png 688w"></figure><p>Best to match the dimension to these. When you check the bullet, only <strong>Social media image </strong>requires that you don&apos;t put any text or logo on it.</p><h3 id="screenshots-dimension">Screenshots dimension</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-57.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1378" height="434" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-57.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-57.png 1000w, https://gametorrahod.com/content/images/2021/12/image-57.png 1378w" sizes="(min-width: 720px) 720px"></figure><p>This one is a bit trickier, since it says &quot;min width 1200 px&quot;. So what size should we use?</p><p>The answer is <strong>1950 * 1300</strong>. The reason being that these images are shown after <strong>Cover Image </strong>in the previous section in the carousel UI. If they are of different dimension than this, (grey) letterbox on top and bottom would be added and the image is centered vertically.</p><h3 id="how-the-social-image-looks-like-as-ogimage">How the Social Image looks like as <code>ogimage</code></h3><p>When people share the Asset Store link via Facebook or Twitter, the Social Image (1200 * 630) (that they told you not to add any text) would be used as <code>ogimage</code> in the &quot;card&quot;.</p><p>Here is the original inside image editor program.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2022/01/image.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1310" height="738" srcset="https://gametorrahod.com/content/images/size/w600/2022/01/image.png 600w, https://gametorrahod.com/content/images/size/w1000/2022/01/image.png 1000w, https://gametorrahod.com/content/images/2022/01/image.png 1310w" sizes="(min-width: 720px) 720px"></figure><p>Here are the results : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2022/01/image-2.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="846" srcset="https://gametorrahod.com/content/images/size/w600/2022/01/image-2.png 600w, https://gametorrahod.com/content/images/size/w1000/2022/01/image-2.png 1000w, https://gametorrahod.com/content/images/size/w1600/2022/01/image-2.png 1600w, https://gametorrahod.com/content/images/2022/01/image-2.png 2100w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2022/01/image-3.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="652" height="476" srcset="https://gametorrahod.com/content/images/size/w600/2022/01/image-3.png 600w, https://gametorrahod.com/content/images/2022/01/image-3.png 652w"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2022/01/image-1.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="939" srcset="https://gametorrahod.com/content/images/size/w600/2022/01/image-1.png 600w, https://gametorrahod.com/content/images/size/w1000/2022/01/image-1.png 1000w, https://gametorrahod.com/content/images/size/w1600/2022/01/image-1.png 1600w, https://gametorrahod.com/content/images/2022/01/image-1.png 2088w" sizes="(min-width: 720px) 720px"></figure><p>As far as I tested, that dimension translates quite well!</p><h3 id="page-indicator-on-screenshots">Page indicator on screenshots</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-59.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="977" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-59.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-59.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/12/image-59.png 1600w, https://gametorrahod.com/content/images/size/w2400/2021/12/image-59.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>In that 1950 * 1300 pixels, Unity will put page indicator <strong>near the bottom edge</strong> (Including the Cover Image). It is best that you don&apos;t fill important graphics to the brim especially if they are text. In my product here, I pad only the bottom edge up a bit.</p><p>The <strong>padding </strong>from the bottom edge of your graphic to the bottom edge of that page indicator is <strong>35 pixels</strong>. The height of that page indicator is <strong>48 pixels. </strong>Counting padding 2 times plus the height, total padding should be 35 + 48 + 35 = <strong>118 pixels </strong>to get padding similar to my example.</p><h3 id="folded-technical-details">Folded technical details</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-58.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="2000" height="1232" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-58.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-58.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/12/image-58.png 1600w, https://gametorrahod.com/content/images/2021/12/image-58.png 2208w" sizes="(min-width: 720px) 720px"></figure><p>Previously, Asset Store not only require us to write HTML tags to style things and also it only got one description area. Now they have added &quot;Technical details&quot; section you can add more texts.</p><p>To keep in mind that this section is folded by default. So make sure to put all the important ones in the &quot;Description&quot;, like your contact details.</p><h3 id="publishing-queue">Publishing queue</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/12/image-60.png" class="kg-image" alt="Publish Asset Store UPM package on which LTS version?" loading="lazy" width="1168" height="730" srcset="https://gametorrahod.com/content/images/size/w600/2021/12/image-60.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/12/image-60.png 1000w, https://gametorrahod.com/content/images/2021/12/image-60.png 1168w" sizes="(min-width: 720px) 720px"></figure><p>If your package is new, it takes a month to be reviewed. (As opposed to an update which is faster.) The new Publisher Portal also show where you are in the queue, you can edit the package without losing queue position. However, I had seen my queue number goes <strong>up, </strong>so don&apos;t take this number too seriously.</p>]]></content:encoded></item><item><title><![CDATA[UnityEvent Serialization Research]]></title><description><![CDATA[I always forget what is the criteria so UnityEvent could target on the dropdown. By writing this article I could come back and read instead of trial-and-error!]]></description><link>https://gametorrahod.com/unityevent-serialization-research/</link><guid isPermaLink="false">623c5d7df445b7063911bf56</guid><category><![CDATA[Unity]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Mon, 04 Oct 2021 15:55:30 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fGV2ZW50fGVufDB8fHx8MTYzMzM2Mjg4OA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fGV2ZW50fGVufDB8fHx8MTYzMzM2Mjg4OA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="UnityEvent Serialization Research"><p>This is something I always forget when designing a new API that I want to be compatible with <code>UnityEvent</code> target serialization. I would like to end that, by writing this article so I could come back and read instead of blindly trial-and-error again to no avail.</p><h2 id="what-is-unityevent-serialization">What is <code>UnityEvent</code> serialization?</h2><p>Everyone knows that declaring <code>public</code> will &quot;expose the variable&quot;.</p><p>More correct term is that it mark that field for <strong>serialization. </strong>Serialization means the data could be remembered in the asset or in the scene instead of starting from zero. It is the data you can input to that exposed slot that get <strong>saved.</strong></p><p>Then advanced Unity users know that <code>[SerializeField]</code> property is a way to also allows other access modifier not <code>public</code> to be serialized.</p><p>Finally even more advanced Unity user knows that <code>UnityEvent</code> is equivalent to C# <code>Action</code> but more awesome because it could be serialized! In other words, you get a nice editor that allows assigning invoke targets, which stays there nicely.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="816" height="60" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image.png 600w, https://gametorrahod.com/content/images/2021/10/image.png 816w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-1.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="956" height="322" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-1.png 600w, https://gametorrahod.com/content/images/2021/10/image-1.png 956w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-2.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="954" height="434" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-2.png 600w, https://gametorrahod.com/content/images/2021/10/image-2.png 954w" sizes="(min-width: 720px) 720px"></figure><p>Despite an editor feature this nice available, I still see tons of Unity developers intentionally not using <code>Button</code>&apos;s nice on click event box that appears in the editor and instead assign everything by code even though they are only one-time assignment. A month or so later, it is impossible to trace which button do what because they are all empty in the editor, and who know which code assign or replace the listener at which moment!</p><p>Main topics here is that what would be the criteria to make it appear in the dropdown? Surely, these aren&apos;t everything that <code>Camera</code> has to offer.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-4.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1294" height="1752" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-4.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-4.png 1000w, https://gametorrahod.com/content/images/2021/10/image-4.png 1294w" sizes="(min-width: 720px) 720px"></figure><h3 id="what-does-the-serialization-looks-like">What does the serialization looks like?</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-3.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1574" height="976" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-3.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-3.png 1000w, https://gametorrahod.com/content/images/2021/10/image-3.png 1574w" sizes="(min-width: 720px) 720px"></figure><p>Notice that it &quot;remember&quot; the target with <strong>assembly-qualified class name. </strong>This has some concerns : </p><ul><li>If you change the <strong>class name </strong>will everything breaks?</li><li>If you move the class to different <code>.asmdef</code> will it also breaks?</li><li>Remembered <strong>argument</strong> is also assembly qualified full name. If Unity is evil and change assembly name of <code>UnityEngine</code> then would it breaks the universe?</li><li>If you refactor target method&apos;s name, I think it will breaks?</li></ul><h2 id="argument-editor-is-more-awesome-than-you-think">Argument editor is more awesome than you think</h2><p>You see the check box above for booleans. It also supports number box and string box. But less known feature (probably) is that it also supports an asset picker!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-6.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="992" height="196" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-6.png 600w, https://gametorrahod.com/content/images/2021/10/image-6.png 992w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-5.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1088" height="976" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-5.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-5.png 1000w, https://gametorrahod.com/content/images/2021/10/image-5.png 1088w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-7.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1382" height="540" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-7.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-7.png 1000w, https://gametorrahod.com/content/images/2021/10/image-7.png 1382w" sizes="(min-width: 720px) 720px"></figure><p>This is really useful to not have little pieces of audio playing code bits littering all over. Also it is extremely good with <strong>prefab workflow. </strong>It shows the blue override bar correctly. The prefab variant can add more entries, keep inheriting entries from root prefab, etc. It is critical with the new Unity Localization package which it uses event targeting extensively, all my texts are a variant of one root text prefab! But it is not our interest right now.</p><p>The serialization :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-8.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1626" height="586" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-8.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-8.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-8.png 1600w, https://gametorrahod.com/content/images/2021/10/image-8.png 1626w" sizes="(min-width: 720px) 720px"></figure><p>You also notice here that it is limited to only 5 types : object, int, float, string, bool. But the object type is so flexible and is my favorite. You should abuse it!</p><h2 id="when-will-things-break">When will things break</h2><p>See that <code>Assembly-CSharp</code> over there? I thought I could instantly screw up the serialization simply by moving the <code>ConnectionTarget</code> to different assembly.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-9.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="478" height="156"></figure><p>But turns out it is still fine.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-10.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="952" height="292" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-10.png 600w, https://gametorrahod.com/content/images/2021/10/image-10.png 952w" sizes="(min-width: 720px) 720px"></figure><p>Seems like the <strong>priority goes to the class name first</strong> and assembly is there to tie-break. If I rename the class...</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-11.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1062" height="344" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-11.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-11.png 1000w, https://gametorrahod.com/content/images/2021/10/image-11.png 1062w" sizes="(min-width: 720px) 720px"></figure><p>Ok finally it breaks!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-12.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="982" height="318" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-12.png 600w, https://gametorrahod.com/content/images/2021/10/image-12.png 982w" sizes="(min-width: 720px) 720px"></figure><p>But you can still save its life. By doing things in this scene and save it, it will <strong>not try to nullify invalid serialization. </strong>Therefore when you realized you broken the link as far as the last month, it could still be saved somehow as old serialization is still there.</p><p>I could remove the <code>z</code> in the class name, and the serialization <strong>could come back to life</strong>.</p><p>In a way, this non-GUID serialization is both blessing and a curse. Sometimes it makes you unable to refactor. But at the same time it allows certain flexibility... once in my project, I got into deep trouble and break everything in the project while refactoring!! I fixed the problem by scanning all YAML files and perform string replace of these class names and an entire project is saved. Thank god.</p><h2 id="dynamic-events">Dynamic events</h2><p>It means that normally <code>UnityEvent</code> requires an invoker to <strong>specify nothing, </strong>the argument is <strong>fixed </strong>by whatever serialized.</p><p>Therefore <strong>dynamic </strong>means that the argument is specified <strong>by the invoker</strong> instead. Unity supports this by providing you <code>UnityEvent&lt;T&gt;</code>, <code>UnityEvent&lt;T1,T2&gt;</code>, .. and so on. Up to 4 dynamic parameters. (Note that static one only supports 1 argument that you can type in Unity Inspector.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-13.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="644" height="492" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-13.png 600w, https://gametorrahod.com/content/images/2021/10/image-13.png 644w"></figure><p>But the catch is Unity can&apos;t serialize generic classes, it must be a solid class known ahead of time. How to make it &quot;solid&quot; is like this. It could be <code>private</code> just fine too but remember to put on that <code>[Serializable]</code> (Unlike fields, a <code>public class</code> is not automatically serializable.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-14.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1310" height="750" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-14.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-14.png 1000w, https://gametorrahod.com/content/images/2021/10/image-14.png 1310w" sizes="(min-width: 720px) 720px"></figure><p>Conveniently, Unity shows eligible dynamic event target up top. Selecting this no longer show <code>AudioClip</code> selection box, because invoker will supply it.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-15.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1072" height="462" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-15.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-15.png 1000w, https://gametorrahod.com/content/images/2021/10/image-15.png 1072w" sizes="(min-width: 720px) 720px"></figure><p>You can still ignore the dynamic event and go back to use the static one as well.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-16.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1384" height="272" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-16.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-16.png 1000w, https://gametorrahod.com/content/images/2021/10/image-16.png 1384w" sizes="(min-width: 720px) 720px"></figure><h2 id="only-public-works-">Only <code>public</code> works (??)</h2><p>This is the first criteria to get it on the dropdown. Supposed we have 3 versions like this :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-20.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1312" height="588" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-20.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-20.png 1000w, https://gametorrahod.com/content/images/2021/10/image-20.png 1312w" sizes="(min-width: 720px) 720px"></figure><p>Only <code>public</code> works. There is no magic attribute like <code>[SerializeMethod]</code> to paste to make it work with <code>internal</code>. This is quite sad for C# freaks who like an extra tidy code.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-18.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1068" height="906" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-18.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-18.png 1000w, https://gametorrahod.com/content/images/2021/10/image-18.png 1068w" sizes="(min-width: 720px) 720px"></figure><p>Only the <code>public</code> one appears...</p><h3 id="can-we-hack-it">Can we hack it?</h3><p>Where&apos;s the fun in playing fair, right? What if I just go ahead and edit the serialization like this. If you love serialization by name so much, then get wrecked.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-19.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1418" height="582" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-19.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-19.png 1000w, https://gametorrahod.com/content/images/2021/10/image-19.png 1418w" sizes="(min-width: 720px) 720px"></figure><p>Turns out it works (lmao), but do you want to sacrifice usability in Unity editor just to call <code>internal</code> or <code>private</code> methods?</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-21.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="970" height="312" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-21.png 600w, https://gametorrahod.com/content/images/2021/10/image-21.png 970w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-22.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="644" height="232" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-22.png 600w, https://gametorrahod.com/content/images/2021/10/image-22.png 644w"></figure><p>It even lights up in JetBrains Rider!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-23.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1302" height="600" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-23.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-23.png 1000w, https://gametorrahod.com/content/images/2021/10/image-23.png 1302w" sizes="(min-width: 720px) 720px"></figure><p>You can also &quot;hack&quot; in the editor by entering debug mode. To be honest the &quot;debug&quot; editor is kinda nice! </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-24.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="970" height="946" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-24.png 600w, https://gametorrahod.com/content/images/2021/10/image-24.png 970w" sizes="(min-width: 720px) 720px"></figure><p>I am designing <strong>an Asset Store </strong>package however, so it is unacceptable that user must hack like this in order to make my component compatible with <code>UnityEvent</code>. I have no choice but to play with <code>public</code> rule.</p><p>Other than that, you decide for yourself is the code hygiene of not using <code>public</code> and still be able to call it from <code>UnityEvent</code> important to you or not.</p><h2 id="only-1-static-argument-allowed">Only 1 static argument allowed</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-38.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1872" height="588" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-38.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-38.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-38.png 1600w, https://gametorrahod.com/content/images/2021/10/image-38.png 1872w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-39.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1274" height="822" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-39.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-39.png 1000w, https://gametorrahod.com/content/images/2021/10/image-39.png 1274w" sizes="(min-width: 720px) 720px"></figure><p>The static event can only call 1 argument methods. You can kinda confirm this by looking at the internal fields of event : the <code>arguments</code> may have an <code>s</code>, but it stands for holding 5 different type of arguments <strong>in 1 argument</strong>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-40.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1246" height="654" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-40.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-40.png 1000w, https://gametorrahod.com/content/images/2021/10/image-40.png 1246w" sizes="(min-width: 720px) 720px"></figure><h2 id="what-if-we-have-mismatched-parameters-than-required">What if we have mismatched parameters than required</h2><p>This event has 2 dynamic arguments <code>AudioClip</code> <strong>and </strong><code>float</code>. I want to try targeting the one with only <code>AudioClip</code>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-28.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1056" height="828" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-28.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-28.png 1000w, https://gametorrahod.com/content/images/2021/10/image-28.png 1056w" sizes="(min-width: 720px) 720px"></figure><p>It <strong>does not work</strong>. Unity doesn&apos;t know how to ignore extra arguments.</p><p>What about less argument? Call this method but with the dynamic event with only one <code>AudioClip</code>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-31.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1306" height="238" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-31.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-31.png 1000w, https://gametorrahod.com/content/images/2021/10/image-31.png 1306w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-32.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="998" height="894" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-32.png 600w, https://gametorrahod.com/content/images/2021/10/image-32.png 998w" sizes="(min-width: 720px) 720px"></figure><p>Nope, nothing appears. Just in case, the perfectly matched one of course works.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-33.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1090" height="470" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-33.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-33.png 1000w, https://gametorrahod.com/content/images/2021/10/image-33.png 1090w" sizes="(min-width: 720px) 720px"></figure><h3 id="what-about-optional-arguments">What about optional arguments?</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-34.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1296" height="246" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-34.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-34.png 1000w, https://gametorrahod.com/content/images/2021/10/image-34.png 1296w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-35.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1052" height="876" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-35.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-35.png 1000w, https://gametorrahod.com/content/images/2021/10/image-35.png 1052w" sizes="(min-width: 720px) 720px"></figure><p>No! It is not that smart! You better think the optional argument as a real argument.</p><p>But this will place heavy restriction to package API design as well, as when you finally get a lot of smart optional arguments and overload resolution planned to work beautifully (e.g. just one <code>Play</code> method name that works in many ways), then you want to make those compatible with <code>UnityEvent</code> suddenly there is a wrench throw in.</p><p>A way to get out is to make a new entry point just for the <code>UnityEvent</code>, but you have to invent a new name which may get ugly. (e.g. suspiciously wordy <code>PlayAudio</code> in addition to <code>Play</code>, just so <code>UnityEvent</code> has something to connect to.) I do this in my package because it can&apos;t be helped.</p><h3 id="can-we-hack-it-1">Can we hack it?</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-36.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1304" height="682" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-36.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-36.png 1000w, https://gametorrahod.com/content/images/2021/10/image-36.png 1304w" sizes="(min-width: 720px) 720px"></figure><p>It has just <code>AudioClip</code>, but method name is edited to the one that also requires <code>float</code> (or optional <code>float</code>)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-37.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1302" height="298" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-37.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-37.png 1000w, https://gametorrahod.com/content/images/2021/10/image-37.png 1302w" sizes="(min-width: 720px) 720px"></figure><p>It does not work. (Entering Play Mode and invoke will not do anything when &quot;Missing&quot;, no error also.)</p><h2 id="if-there-is-a-return-value">If there is a return value?</h2><p>In my Asset Store package, I want the play to also return a value. This could be used to stop the already played audio.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-25.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1592" height="1334" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-25.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-25.png 1000w, https://gametorrahod.com/content/images/2021/10/image-25.png 1592w" sizes="(min-width: 720px) 720px"></figure><p>Unfortunately, having non <code>void</code> return value disqualifies everything. Including the <code>public</code> one.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-26.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1054" height="876" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-26.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-26.png 1000w, https://gametorrahod.com/content/images/2021/10/image-26.png 1054w" sizes="(min-width: 720px) 720px"></figure><p>Same as dynamic version : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-27.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1066" height="994" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-27.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-27.png 1000w, https://gametorrahod.com/content/images/2021/10/image-27.png 1066w" sizes="(min-width: 720px) 720px"></figure><h3 id="can-we-hack-it-2">Can we hack it?</h3><p>Perform the same hack by directly edit the method name. Note that the argument is perfectly compatible, exactly 1 <code>AudioClip</code>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-29.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="988" height="760" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-29.png 600w, https://gametorrahod.com/content/images/2021/10/image-29.png 988w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-30.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="2000" height="400" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-30.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-30.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-30.png 1600w, https://gametorrahod.com/content/images/size/w2400/2021/10/image-30.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Whoops, I was caught this time!</p><p>Though, we now additionally know that it uses <code>System.Delegate.CreateDelegate</code> underneath, which is standard C# rather than a Unity-thing. So if we know how that works, then we know what could and could not be hacked. It could be assumed now that Unity engine coded such that it binds to <code>void</code> return value of the target class, but there is no criteria on access modifier so the hack was successful.</p><p>Interestingly <a href="https://docs.microsoft.com/en-us/dotnet/api/system.delegate.createdelegate">CreateDelegate</a> overloads has <code>firstArgument</code> criteria, so maybe this implies if we hack the 2nd argument it could get through. But I am too tired to try that now.</p><h2 id="no-interface-arguments-but-subclass-ok">No <code>interface</code> arguments, but subclass OK</h2><p>If you made your API to be nicely handling <code>interface</code> of your custom class, then bad news the serializer don&apos;t know how to deal with it.</p><p>But subclass is OK! This allows the asset picker to pick <strong>any subclass </strong>from the base class specified in the method&apos;s signature. This allows you to use <code>abstract class</code> as well. In this image, <code>AuditoPlayableBase</code> is an <code>abstract class</code>.</p><p>In this picture, that <code>public void</code> is just a scapegoat for <code>UnityEvent</code> targeting. It is using a <code>class</code> not <code>interface</code> so Unity know how to serialize the static argument. Moreover it has <code>void</code> return and not <code>AuditoHandle</code> I actually want to use.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-42.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="2000" height="478" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-42.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-42.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-42.png 1600w, https://gametorrahod.com/content/images/2021/10/image-42.png 2166w" sizes="(min-width: 720px) 720px"></figure><p>The object picker shows all the choices that subclassed from <code>AuditoPlayableBase</code> that aren&apos;t <code>abstract</code> anymore. This is a really nice developer experience for the user.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-41.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1696" height="600" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-41.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-41.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-41.png 1600w, https://gametorrahod.com/content/images/2021/10/image-41.png 1696w" sizes="(min-width: 720px) 720px"></figure><p>This leads to <strong>weird</strong> class tree design where an <code>abstract class</code> is there just so Unity could know how to serialize, but the real deal is the <code>interface</code> I want to use everywhere. The best solution maybe to hide the <code>interface</code> behind <code>abstract class</code>, and <code>abstract class</code> may essentially do nothing because the <code>interface</code> had already locked in everything. (lol)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-43.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1806" height="324" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-43.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-43.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-43.png 1600w, https://gametorrahod.com/content/images/2021/10/image-43.png 1806w" sizes="(min-width: 720px) 720px"></figure><h2 id="can-call-static">Can call <code>static</code></h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-46.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1730" height="1088" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-46.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-46.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-46.png 1600w, https://gametorrahod.com/content/images/2021/10/image-46.png 1730w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-47.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1116" height="414" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-47.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-47.png 1000w, https://gametorrahod.com/content/images/2021/10/image-47.png 1116w" sizes="(min-width: 720px) 720px"></figure><p>I found about this by mistake. Though it make sense that it should be able to, I didn&apos;t think Unity would actually let me to do this lol.</p><p>This is given all the previous criteria are passed, so must be returning <code>void</code>. It can take arguments just fine, like discussed.</p><h2 id="implications-in-api-design">Implications in API design</h2><p>Because the target&apos;s name will be <strong>forever </strong>serialized in the client&apos;s code, you must make damn sure you don&apos;t refactor the name to something else. This already applies to all your <code>public</code> code, but breaking people&apos;s serialized event target won&apos;t trigger compilation error. Scary stuff!</p><p>And you have to be more skillfully sidestep everything. Take a look at this :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-44.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="1924" height="702" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-44.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-44.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-44.png 1600w, https://gametorrahod.com/content/images/2021/10/image-44.png 1924w" sizes="(min-width: 720px) 720px"></figure><ul><li>The first requirement is that I want the <code>static</code> method <code>Play</code> with complete overload + default argument, so I could type <code>NonpositionalAudio.Play</code> to use it. It has return value too.</li><li>Then I want to make an instance <code>public</code> <code>Play</code> method to service those that wants to use <code>UnityEvent</code>. First I have to sacrifice return type, then also <code>interface</code> is not usable.</li><li>Lastly there is one more <code>Play</code> overload I want to be able to use with events. But if that is named <code>Play</code>, it will cause infinite recursion to itself instead of resolving to the <code>static</code> method. Now I have to name it something else.</li></ul><p>And this 5 <code>Play</code> : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/10/image-45.png" class="kg-image" alt="UnityEvent Serialization Research" loading="lazy" width="2000" height="533" srcset="https://gametorrahod.com/content/images/size/w600/2021/10/image-45.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/10/image-45.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/10/image-45.png 1600w, https://gametorrahod.com/content/images/2021/10/image-45.png 2160w" sizes="(min-width: 720px) 720px"></figure><p>Normally the bottom 2 are enough to handle everything I want, but if I want it to be compatible with events, I need 3 more above with no return value. Also I cannot just use default arguments, I need to explicitly add all the combinations. Of course I cannot use <code>interface</code> too. Lastly, I can&apos;t name the top 3 <code>Play</code> anymore as it would cause similar problem as before. So, I name them <code>PlayAudio</code> instead. </p><p>(Other idea includes <code>PlayFromEvent</code>, to explicitly excuse that there is no return value because it is &quot;for event&quot;, but I think that is weirder.)</p>]]></content:encoded></item><item><title><![CDATA[34-key Kalimba music theory analysis]]></title><description><![CDATA[Seeds Kalimba "Pisces" is a pretty interesting 34-key Kalimba! I love when a brand try to pioneer something and there are not much information anywhere else, I wanted to be at the frontier and experiment.]]></description><link>https://gametorrahod.com/34-keys-kalimba-seeds-pisces/</link><guid isPermaLink="false">623c5d7df445b7063911bf53</guid><category><![CDATA[Art / Audio]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Wed, 01 Sep 2021 08:10:18 GMT</pubDate><media:content url="https://gametorrahod.com/content/images/2021/09/IMG_0624.JPG" medium="image"/><content:encoded><![CDATA[<img src="https://gametorrahod.com/content/images/2021/09/IMG_0624.JPG" alt="34-key Kalimba music theory analysis"><p>Seeds Instruments made a pretty interesting 34-key Kalimba called the Pisces!</p><ul><li><a href="https://seedskalimbas.com/">https://seedskalimbas.com/</a></li><li><a href="https://www.facebook.com/SeedsKalimba/">Seeds Kalimba | Facebook</a></li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/IMG_0624-1.JPG" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="1500" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/IMG_0624-1.JPG 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/IMG_0624-1.JPG 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/IMG_0624-1.JPG 1600w, https://gametorrahod.com/content/images/size/w2400/2021/09/IMG_0624-1.JPG 2400w" sizes="(min-width: 720px) 720px"></figure><p>I wanted a Kalimba to add interesting calming arps when composing game music for long. As soon as I see this arrangement which is currently not many brands are doing, I felt adventurous and wanted to explore. I love when a brand try to pioneer something and there are not much information anywhere else, I wanted to be at the frontier and experiment. Just like how I was messing around with Unity DOTS.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/IMG_0625.JPG" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="1500" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/IMG_0625.JPG 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/IMG_0625.JPG 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/IMG_0625.JPG 1600w, https://gametorrahod.com/content/images/size/w2400/2021/09/IMG_0625.JPG 2400w" sizes="(min-width: 720px) 720px"></figure><p>And so with magic of today&apos;s e-commerce, it finally appears before me. Let&apos;s get the research started.</p><p>In this article, I analyze the implication of this 34-key layout what possibilities are opened up. This assumes we will not re-tune the Kalimba to fit the song we want to play. We want the added flexibility of more tines to counter that!</p><p>If you wondered why this kind of article appears in a Unity game programming blog like this, I picked up some instruments in lockdown and the diagrams here were made with Unity..! And also just so you know the Kalimba sound is leaning nice into game soundtrack territory. This sonic character could be in calming village music or fantasy forest theme.</p><p>This Unity project I quickly whipped up in about an hour can ask all the tines to number itself according to either major or minor scale interval starting from any note and hide unrelated ones, so we could reveal interesting properties visually.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="1242" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image.png 1600w, https://gametorrahod.com/content/images/size/w2400/2021/09/image.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>This article requires a bit of music theory knowledge. (How many notes are there and their distances, key, chords, interval, diatonic, chromatic.) This maybe the first article taking the 34-key Kalimba seriously on the net since I can&apos;t find anything elsewhere, the 17-key version is so popular it dominates the scene. Therefore, let&apos;s start at 17-key.</p><h2 id="recap-the-17-key-version-s-arrangement">Recap the 17-key version&apos;s arrangement</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-2.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="888" height="302" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-2.png 600w, https://gametorrahod.com/content/images/2021/08/image-2.png 888w" sizes="(min-width: 720px) 720px"></figure><p>The popular 17-key version is <strong>diatonic centered around major scale.</strong> It is literally centered, the root note is on the center tine. So it means either you bring the tuning hammer with you all the time in order to play all songs or you transpose any other song to this key you choose. Note that the hammer can&apos;t fix everything since tine length gets more limited on the edge, not only we can&apos;t get higher, shorter tines has less decay time as well and require more force to compensate.</p><p>It has alternating tines left and right to form a major scale starting from the center tine going left first. Numbers in the picture are note/key agnostic, it is the member of your major key.</p><p>For example, in C key Kalimba 1 is C, 6 is A. In music theory, there is no &quot;black&quot; notes since actually we have 12 notes lined up <strong>linearly</strong>. These 7 notes C D E F G A B C we see as a result of C major scale here looks linear but <strong>they aren&apos;t,</strong> only because of English alphabet tricks your eye but each one is 2 semitones apart, except BC and EF. So 7 notes looping can&apos;t be equally spaced, we need 12 notes and 2 kinds of distance : 1 semi + 2 semi. This is <a href="https://en.wikipedia.org/wiki/Maximal_evenness">maximal evenness</a>. In parallel world, we might have A B C D E F G H I J K L as notes and there is no sharp or flats since those would give the nearby alphabet without exceptions, if the English guys discovered chromatic before diatonic or something.</p><h3 id="easy-tonic-chords-glissando">Easy tonic chords glissando</h3><p>The alternating tines are probably for ergonomics of distributing notes to both thumbs but that is not all, it allows single thumb 3 notes glissando to form <strong>tonic chords </strong>perfectly. Tonic chords means not just any chord but they could &quot;work&quot; together to create a song in a given key of music, creating tension and resolve back.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-01-at-11.25.14.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1462" height="490" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/Screen-Shot-2021-09-01-at-11.25.14.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/Screen-Shot-2021-09-01-at-11.25.14.png 1000w, https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-01-at-11.25.14.png 1462w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-01-at-11.26.54.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1464" height="490" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/Screen-Shot-2021-09-01-at-11.26.54.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/Screen-Shot-2021-09-01-at-11.26.54.png 1000w, https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-01-at-11.26.54.png 1464w" sizes="(min-width: 720px) 720px"></figure><p>The green arrows are tonic major chords and red arrows are tonic minor chords. You can easily get your : I, ii, iii, IV, V, vi, vii chord of the tuning of your Kalimba with a gentle slide starting from the number that you want and going outwards. It automatically skips notes of an interval too close to the previous one automatically, which that would sound dissonance. To play the popular chord progression I -&gt; IV -&gt; V -&gt; I, just find any number printing on the tines literally saying 1, 4, 5, 1 then slide away.</p><p>Piano players know keys close by could be used to form interesting uncommon chords to spice things up. Kalimba made the spice impossible when randomly brushing around. As a result if you left the Kalimba for other not knowing music to mess around, it would still sounds quite pleasant unlike if someone randomly press piano keys.</p><p>Also if you want to play ii minor chord, you can do 2 4 6 on the left side, but it also is available on the right side higher up! Talking about extremely lucky coincidence that there are 7 notes so by alternating left and right we get both even and odd numbers in one side. The 17-key arrangement is a very good idea, no wonder why it is popular.</p><p>I skipped 7 2 4, the diminished sounding chords, in the image to highlight all available major and minor chords. Of course you can play that for occasional spooky sound.</p><h3 id="4-note-chords">4-Note Chords</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-01-at-11.32.50.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1470" height="492" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/Screen-Shot-2021-09-01-at-11.32.50.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/Screen-Shot-2021-09-01-at-11.32.50.png 1000w, https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-01-at-11.32.50.png 1470w" sizes="(min-width: 720px) 720px"></figure><p>We are even luckier that brushing 4 consecutive tines automatically get us the bluesy sounding <strong>seventh chords </strong>like 1 3 5 7 one, or 2 4 6 1 one.</p><ul><li>Blue arrows are <strong>major seventh chords. </strong>Notice that we have 2 &quot;1 3 5 7&quot; position to play.</li><li>Magenta arrows are <strong>minor seventh chords</strong>. Notice that we only have room to play the seventh version of vi chord, the 6 1 3 5, on the left side since the other one on the right starts late and the &quot;seventh&quot; component fell off the edge...</li><li>We can play <strong>dominant seventh </strong>chord on the yellow arrow too, which would play G7 in this case.</li></ul><p>Of course you can continue to get the rich <strong>ninth chord </strong>sound, etc.</p><h3 id="range">Range</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-2.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="888" height="302" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-2.png 600w, https://gametorrahod.com/content/images/2021/08/image-2.png 888w" sizes="(min-width: 720px) 720px"></figure><p>Notice that the 1 note spans about 2 octaves, then we get a little 2 and 3 over and thats it.</p><p>If you play major key song in the same key as Kalimba, the &quot;home&quot; is probably 1 on the left side so you can go both up and down. Depending on song, if it gets emotional it maybe higher than then range that you need to move the &quot;home&quot; down to the center 1. (Which may gets too low to your liking, but thats &quot;Kalimba arrangement&quot; for you.)</p><h3 id="another-major-scale-choice-mixolydian-mode-like-">Another major scale choice (Mixolydian mode-like)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-5.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="896" height="304" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-5.png 600w, https://gametorrahod.com/content/images/2021/08/image-5.png 896w" sizes="(min-width: 720px) 720px"></figure><p>We can <strong>somewhat</strong> play the major scale of 5th member of the Kalimba&apos;s key too! In the case of C Kalimba, then this is how G major scale would play out. (G is perfect fifth from C.)</p><p>&quot;Somewhat&quot; means that <strong>one tine will be wrong</strong> if you really play Mixolydian of C scale compared to the real G major scale. So please play almost Mixolydian.</p><p>That tine is the &quot;7&quot; note. This note is a component of quite important V chord (5 7 1) so keep that in mind, and also iii (5 3 7). The vii (7 2 4) is rarely used, luckily. This note 7 is also used in a melody to lead into 1 so this will impact some songs doing that.</p><p>The wrong tine by the way, if you go ahead and play it in place of 7 in the context of G major scale then it is a sharpened 6 (or flattened 7) instead. You can use this fact to play new chords that has sharpened 6 as a component.</p><p>Also the flattened 7 wrong note has a name, the <a href="https://en.wikipedia.org/wiki/Subtonic">subtonic</a>. It also has many uses different from the correct one (<a href="https://en.wikipedia.org/wiki/Leading-tone">leading-tone</a>). I would not consider playing in this position that &quot;wrong&quot;.</p><p>Note that in harmonica, trying to play G major scale on C harmonica is called the 2nd position or the &quot;cross harp&quot;. The blues player play in this position, the reason is because the &quot;wrong&quot; note happen to be exactly what blues want, it feels bluesy. So when playing G major blues song, harmonica finds a C major one and play in this position.</p><h3 id="yet-another-major-scale-choice-lydian-mode-like-">Yet another major scale choice (Lydian mode-like)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-6.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="934" height="322" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-6.png 600w, https://gametorrahod.com/content/images/2021/08/image-6.png 934w" sizes="(min-width: 720px) 720px"></figure><p>Closely related to the perfect fifth above root is the perfect fourth, or in other words going counter clockwise in the Circle of fifths instead. In C Kalimba, the 4th member is F. This is how it looks like if we make the 4 note instead our new home.</p><p>If you really go ahead and play Lydian of C as a makeshift F major scale, you will also hit <strong>one wrong note </strong>in the same reason as the G major scale vs. C major scale but &quot;to the other way&quot;. This time the missing note is the 4 note, which is a component of ii (2 4 6), IV (4 6 1), vii (7 2 4). The vii chord is rare so we are lucky, but missing ii or IV chord maybe problematic.</p><h3 id="minor-scale-play-aeolian-mode-">Minor scale play (Aeolian mode)</h3><p>You can play the <strong>relative minor scale</strong> of your Kalimba&apos;s key for some emotional songs and get the same glissando chord benefit. The home moves to note 6 and if you choose the 6 on the left side as your home then you get a bit higher upper range than the major scale playing.</p><p>Let&apos;s see if we look at the note 6 as a new 1 for our minor scale, what would it looks like. All tines are correct, naturally!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-7.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="902" height="322" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-7.png 600w, https://gametorrahod.com/content/images/2021/08/image-7.png 902w" sizes="(min-width: 720px) 720px"></figure><p>By shifting the numbers, range are affected. For example there is only 1 instance of 2 4 6 glissando remaining now. Poor II chord! (The II chord switched from vii that was in major scale.)</p><h3 id="playing-in-dorian-or-phrygian-mode-for-more-minor-scale-choices">Playing in Dorian or Phrygian mode for more minor scale choices</h3><p>Same reasoning as when we want to play 2 more major scales with 1 wrong tine, you can do the same starting from A minor scale we can naturally play and go clockwise and counter clockwise. Therefore it is D (note 2 of C) and E (note 3 of C) minor scale we could also somewhat play with 1 missing note.</p><p>Here&apos;s how D minor scale on our C major Kalimba looks like. The missing note is 6. (It was 4 in major scale.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-8.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="940" height="328" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-8.png 600w, https://gametorrahod.com/content/images/2021/08/image-8.png 940w" sizes="(min-width: 720px) 720px"></figure><p>Some observations :</p><ul><li>We have 1 3 5 glissando available to play the home minor chord on both sides.</li><li>We also have 5 7 2 for minor scale version of V chord.</li><li>3 5 7 is also cool.</li><li>7 2 4 on both sides are not so useful in most songs.</li></ul><p>Here&apos;s how E minor scale on our C major Kalimba looks like. The missing note is 2. (It was 7 in major scale.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-9.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="924" height="318" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-9.png 600w, https://gametorrahod.com/content/images/2021/08/image-9.png 924w" sizes="(min-width: 720px) 720px"></figure><p>Some observations :</p><ul><li>We have 1 3 5 glissando available to play the home minor chord on both sides still but the position moved a bit.</li><li>5 7 2 no longer possible. (which is probably quite a big deal since it is a V chord)</li><li>3 5 7 is still possible.</li><li>Now we can do 4 6 1 and 6 1 3 on both sides too.</li></ul><p>Compared to major version, because the missing note is not 7, you don&apos;t get the leading-tone-to-subtonic benefit.</p><h2 id="recap-the-21-key-version-s-arrangement">Recap the 21-key version&apos;s arrangement</h2><p>The extra 4 tines are added for extended <strong>bass </strong>sound.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-8.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1368" height="1372" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-8.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-8.png 1000w, https://gametorrahod.com/content/images/2021/09/image-8.png 1368w" sizes="(min-width: 720px) 720px"></figure><p>They are jammed right in the middle as low 7 5 4 6 tines, so the center is <strong>no longer your root note</strong> of your key. You can still perform glissando chord trick from new bass notes, for example from the new low 4 6 into the original 1, or the new low 5 7 then onto the original 2.</p><p>These are also good idea since now you are able to lay down interesting interval <strong>under</strong> your melody as a representative of chords in the song.</p><h2 id="here-comes-the-37-key-arrangement">Here comes the 37-key arrangement</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-1.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="958" height="534" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-1.png 600w, https://gametorrahod.com/content/images/2021/08/image-1.png 958w" sizes="(min-width: 720px) 720px"></figure><p>The idea is to make a 2-row Kalimba where the first row is exactly like 17-key version (alternating major scale), then move the 4 extra bass tines of the 21-key out of the way to 2nd layer, <strong>then fill the remaining upper tines with a sharpened note of the first layer. (one half step higher)</strong></p><p>This is a diagram from Seeds Kalimba Pisces :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-9.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1206" height="1382" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-9.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-9.png 1000w, https://gametorrahod.com/content/images/2021/09/image-9.png 1206w" sizes="(min-width: 720px) 720px"></figure><h3 id="accidentally-chromatic">Accidentally chromatic</h3><p>Many call this &quot;chromatic Kalimba&quot; but notice that the upper tines aren&apos;t equivalent of black keys on the piano. They may ended up covering all notes chromatically &quot;accidentally&quot; but the upper tines do not care about that, they just want to <strong>sharpen the lower one</strong>.</p><p>First, the upper row 7 5 4 6 are <strong>special,</strong> they are not sharpened tines but your extra bass notes <strong>in the major scale of lower row</strong> that are now made &quot;optional&quot;, inspired by 21-key design. You reach up for them when needed instead of having them breaking the flow of beautiful alternating 17-keys. These rows aren&apos;t adding any chromatic quality from 17-key design that was diatonic.</p><p>Now the sharpened tines are the real deal that adds chromaticity (that is a word?). Because playability is still centered around the lower row having major scale ready for you, using these upper tines for <strong>accidental notes</strong> that music composer added for spice maybe a good idea rather than trying to play in arbitrary keys.</p><p>However remember that among 12 notes in one octave, BC and EF are only 1 half step apart unlike the other guys. Therefore the sharpened 7 is 1 and the sharpened 3 is 4 in all major scale. We can see that the upper tines therefore sometimes include duplicated sound of the lower one. The upper tines are really meant for <strong>sharpening</strong>, not for covering all notes.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gametorrahod.com/content/images/2021/08/image-1.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="958" height="534" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-1.png 600w, https://gametorrahod.com/content/images/2021/08/image-1.png 958w"></figure><h3 id="make-use-of-the-duplicated-sharpened-notes">Make use of the duplicated sharpened notes</h3><p>Having 2 more &quot;1&quot; note and &quot;4&quot; note available up there may comes in handy sometimes.</p><p>You can perform exact pitch unison with 2 same notes to amplify it, or you can <strong>trill </strong>the note with both of your thumb for style like some other instruments!</p><p>The 1 note is likely to sound good as an interval with the other tines in many situation since it is the root of the key that you are playing, assuming you are playing the same key as the Kalimba&apos;s key.</p><p>Any interval pair in your key with 1 and 4 is going to give you some more choices. So look out for them.</p><p>You can also perform this cool trick, <strong>vertical glissando. </strong>But you can only do it on 1 -&gt; 7 and 4 -&gt; 3. There are 4 positions you can do this. If you prepare your other thumb to add 6 and 2 respectively, it sounds quite like believable you just brushed with one thumb! Or you can drag diagonally and play other nearby interval as well. Might looks flashy in your Kalimba video..</p><h3 id="bonus-high-4-note">Bonus high-4 note</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-02-at-13.56.07.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1812" height="874" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/Screen-Shot-2021-09-02-at-13.56.07.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/Screen-Shot-2021-09-02-at-13.56.07.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/Screen-Shot-2021-09-02-at-13.56.07.png 1600w, https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-02-at-13.56.07.png 1812w" sizes="(min-width: 720px) 720px"></figure><p>Note that <strong>the rightmost 4</strong> is not exactly a duplicate since it is on doubly high octave. This give more variation for you to perform unison interval of various instances of 4.</p><p>Perhaps more importantly, it extends your range by 1 note in the key of C as if there is one more tine to the right of the rightmost tine, you just move up instead. This is because 3-&gt;4 is one of two place where we only need 1 semitone up to get to the next scale tone, lucky!</p><p>When playing on minor scale song and you start at 6 (to be your new 1), note 3 (the final lower tine) is the &quot;5&quot; of minor scale. 5th tone of any diatonic scale is the <a href="https://en.wikipedia.org/wiki/Dominant_(music)">dominant</a>, well documented as very important to the song. Therefore this bonus 4 will give you the &quot;6&quot; of minor scale, the <a href="https://en.wikipedia.org/wiki/Submediant">submediant</a>. So when you use the Pisces, you are able to play minor key songs that use this submediant-feel in the composition on high notes, provided that you transpose it to A minor first.</p><p>Most Kalimba stops at high 3 (E6 note, if in C tuning). Be warned that this bonus 4 tine is very short and hard, most low quality Kalimba would not sound great but it works on the Pisces, perhaps thanks to unibody metal saddle design.</p><h3 id="arps-from-bass-tines">Arps from bass tines</h3><p>It moves up the 4 bass tines from 21-key to above. If you want to arp from lower notes, then you cannot just brush your fingers. Instead to play the 4 available triad of your scale degree : 4 5 6 7 (<strong>4</strong> 6 1, <strong>5</strong> 7 2, <strong>6</strong> 1 4, <strong>7</strong> 2 4) starting from the bass tines, and send off to the higher octave, you follow a pattern like this :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-02-at-12.58.34.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1842" height="840" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/Screen-Shot-2021-09-02-at-12.58.34.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/Screen-Shot-2021-09-02-at-12.58.34.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/Screen-Shot-2021-09-02-at-12.58.34.png 1600w, https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-02-at-12.58.34.png 1842w" sizes="(min-width: 720px) 720px"></figure><p>Some observations :</p><ul><li>All chords are one handed from bass tines, but at the moment you hit all 3 notes, the next one will be <strong>on the other hand </strong>which you continue like the usual 17-key format. This works the same as in 21-key format, if you want to arp several times it would alternate sides.</li><li>Because the 3-note chord is splitted up into upper part and lower part, remember that the 4 and 5 chord will play the upper one 2 times, but 6 and 7 will play the lower one 2 times to not get confused.</li><li>2-note glissando as a part of chord maybe possible. If you want more challenge you can even do vertical one. (e.g. in 4 6 1, instead of glissando at 4 6, try 6 1 instead.)</li></ul><h3 id="sharpening-minor-chords-into-major">Sharpening minor chords into major</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-02-at-13.14.55.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1836" height="854" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/Screen-Shot-2021-09-02-at-13.14.55.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/Screen-Shot-2021-09-02-at-13.14.55.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/Screen-Shot-2021-09-02-at-13.14.55.png 1600w, https://gametorrahod.com/content/images/2021/09/Screen-Shot-2021-09-02-at-13.14.55.png 1836w" sizes="(min-width: 720px) 720px"></figure><p>Since the upper tines are strictly sharpening the lower one, every time you play minor chord on the lower tines you can change the 2nd note to the upper one to instead get major chord. (But you can&apos;t glissando, do an arp instead.) Also the upper one must not be one of the special 4 bass tines.</p><p>It will be out of key, but some song use this trick to spice things up. (It sounds emotional in a certain progression, when listener think they get a minor next but instead it is a major.)</p><p>Available chords : 2 (right side), 3 (both sides), 6 (both sides). You cannot sharpen the 2 chord on the left side since it collides with the 7 bass tine, that one is not sharpening your note.</p><p>Inverse is possible, if you play major chords on the upper tines then you can turn it into a minor. But in your Kalimba&apos;s key, it is unlikely that you can play major chord on upper tines. (If you play C# major key on upper tines though this is very possible, explained later.)</p><h3 id="now-we-have-more-scales-for-real-">Now we have more scales for real!</h3><p>Remember how we could only play perfect major key of the key of Kalimba and its relative minor key song, but when we push them further then there are tines missing?</p><p>Now that we have sharpened tines plus the bass tines, let&apos;s see how it turns out!</p><h3 id="mixolydian-like-c-major-g-major-">Mixolydian-like (C major -&gt; G major)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-10.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="950" height="442" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-10.png 600w, https://gametorrahod.com/content/images/2021/08/image-10.png 950w" sizes="(min-width: 720px) 720px"></figure><p>Instead of missing the 7 note now we have <strong>one 7 note </strong>waiting up there on the right side! Therefore it is now possible to play the G major scale on the middle range, or compose missing chords using that one lifeline we now have. We knew the wrong note was a flattened version of the right one, and luckily the sharpened tine give us that up above.</p><p>Note that it is possible to perform 5 on the right side and then <strong>diagonal vertical glissando 7 2 </strong>to perform 5 7 2, the essential V chord sound needed to finally go back home to I chord. Looks like that 7 will be your buddy when playing in this position!</p><p>Also notice that the 4 bass tines covers note 1, 2, 3 of the key on this position. Having deep 1 bass in that position will be very convenient.</p><p>Note that the fact that it is weird that we get only one 7 note in an entire board is because of &quot;wrench&quot; than the upper bass tines threw in. We likely get one more 7 if there is no bass tines there.</p><h3 id="lydian-like-c-major-f-major-">Lydian-like (C major -&gt; F major)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-11.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="946" height="428" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-11.png 600w, https://gametorrahod.com/content/images/2021/08/image-11.png 946w" sizes="(min-width: 720px) 720px"></figure><p>Recall that we were missing the 4 note. The sharpened tines gives us 4 notes on both sides. This make the 4th of home scale very usable!</p><p>Let&apos;s look at the chords that were missing :</p><ul><li>VI (4 6 1) has <strong>diagonal vertical glissando </strong>of 4 1 available on the left and right side, then you just add 6 nearby to get it. The right side is difficult since the upper 4 has short tine.</li><li>Then ii (2 4 6) should be done with alternating thumbs since there are no nearby tines. We get one lucky vertical 2 6 on the bass tine area, if you would like a bassy ii chord.</li><li>Lastly vii (7 2 4) is rarely used anyways.</li></ul><p>Once again the 4 bass tines has 1, 2, 3 of the key. Now 1 is even on dead center!</p><h3 id="aeolian-c-major-a-minor-">Aeolian (C major -&gt; A minor)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-19.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="946" height="418" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-19.png 600w, https://gametorrahod.com/content/images/2021/08/image-19.png 946w" sizes="(min-width: 720px) 720px"></figure><p>What do we get more if we play relative minor? We get 1 and 2 bass to use. Two 6, two 3.</p><h3 id="dorian-like-c-major-a-minor-d-minor-">Dorian-like (C major -&gt; A minor -&gt; D minor)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-12.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="948" height="436" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-12.png 600w, https://gametorrahod.com/content/images/2021/08/image-12.png 948w" sizes="(min-width: 720px) 720px"></figure><p>The 6 note was missing, now we get it on both sides. Unfortunately minor scales will not get the 4 bass tines cover the root note. Instead, we get a 4 and 5 bass tine that maybe useful for going back to the minor home.</p><h3 id="phrygian-like-c-major-a-minor-e-minor-">Phrygian-like (C major -&gt; A minor -&gt; E minor)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-13.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="948" height="436" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-13.png 600w, https://gametorrahod.com/content/images/2021/08/image-13.png 948w" sizes="(min-width: 720px) 720px"></figure><p>The missing note 2 was filled with one tine, at the same spot as 7 that was filled in our Mixolydian-as-new-major case. Music theory!</p><h3 id="upper-tines-keys">Upper tines keys?</h3><p>Inversely, there are possibilities that we could play some other keys that mainly use the new upper tines so we can instead be glissando up there for a lot and occasionally come down! This way 4 bass tines are likely out of key, but let&apos;s explore keys that has many in-key notes on the upper tines.</p><h3 id="c-major-c-major-1-semi-major-">C major -&gt; C# major (+1 semi major)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-14.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="952" height="444" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-14.png 600w, https://gametorrahod.com/content/images/2021/08/image-14.png 952w" sizes="(min-width: 720px) 720px"></figure><p>Playing one half step of your source key is the shortest way to sharpen all the things. And this works to our purpose!</p><ul><li>Useful chords all on upper tines. Left : vi (6 1 3), I (1 3 5), iii (3 5 7), V (5 7 2). Right : V (5 7 2), vii (7 2 4), ii (2 4 6), vi (6 1 3). We have V and vi on both sides. The home chord I is only on one side&apos;s glissando so we will have to pay attention to that.</li><li>Only two 1 notes available, and they are all high pitch. This may force the range to be narrow, you many not be able to play songs that are too dynamic on this position.</li><li>Important to note that if you ignore the 4 bass tines (ok the center one works as a note 3 bass), <strong>all upper tines are correct. </strong>You just need to remember where are the 1 and center yourself around them.</li><li>Bass notes are limited. You only got low 7 and low 3 at your disposal. May need rearranging to brighter style to fit this.</li></ul><p>If you have Seeds Instruments Pisces in C then look for songs in key of C# major. You can comfortably nail about 1.2 octave of this key via the upper tines. Tuning without hammers!</p><p>Another important implication : <strong>1 semitone</strong> <strong>key shift. </strong>Some songs shift keys <strong>up</strong> briefly to be emotional (e.g. Connect, Madoka OP, at the beginning) therefore if you transpose the song to C major (comfortably play on lower tines), then you get an easy shift to C# major by moving to this position on the same Kalimba.</p><h3 id="c-major-g-major-8-semi-major-">C major -&gt; G# major (+8 semi major)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-18.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="944" height="420" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-18.png 600w, https://gametorrahod.com/content/images/2021/08/image-18.png 944w" sizes="(min-width: 720px) 720px"></figure><p>We get interesting clusters here. 6 consecutive notes on the left, 2 sets of 3 notes chord on the right. All 7 notes fell down.</p><h3 id="c-major-f-major-6-semi-major-">C major -&gt; F# major (+6 semi major)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-17.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="952" height="436" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-17.png 600w, https://gametorrahod.com/content/images/2021/08/image-17.png 952w" sizes="(min-width: 720px) 720px"></figure><p>Only one 1 note on the board, this will be challenging! Probably the most difficult among the feasible major scales we can play on 34-key Kalimba.</p><h3 id="c-major-a-minor-10-semi-minor-">C major -&gt; A# minor (+10 semi minor)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-20.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="942" height="430" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-20.png 600w, https://gametorrahod.com/content/images/2021/08/image-20.png 942w" sizes="(min-width: 720px) 720px"></figure><p>This is how to get the most of upper tines on minor key, in other words it is a relative minor of that C# major scale we explored earlier because we have almost the same right notes and same wrong notes except the bass upper tines.</p><p>Bass upper tines are all out of keys. This looks really playable, and there are tons of 2 notes. (Was 7 on the C# major scale earlier.)</p><p>Because this is the same pattern as C# major earlier, you can again perform <strong>1 semitone key shift up </strong>easily from A minor key. To get easy shift on minor key songs, transpose it to A minor then when the song gets emo to A# minor then play on upper tines. Bass notes are 2 and 5 only. Luckily we still have 2 instance of 1 note, with the low 1 almost fell off.</p><h3 id="c-major-f-minor-5-semi-minor-">C major -&gt; F minor (+5 semi minor)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-16.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="952" height="432" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-16.png 600w, https://gametorrahod.com/content/images/2021/08/image-16.png 952w" sizes="(min-width: 720px) 720px"></figure><p>This position gives you the root bass on upper center plus considerable amount of in-key tines on the 2nd layer. This is quite a spacious minor scale with three 1 notes available.</p><h3 id="c-major-d-minor-3-semi-minor-">C major -&gt; D# minor (+3 semi minor)</h3><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/08/image-15.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="948" height="442" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-15.png 600w, https://gametorrahod.com/content/images/2021/08/image-15.png 948w" sizes="(min-width: 720px) 720px"></figure><p>This minor position also have a single 1 3 5 combo in an entire board on the right side, and one more high pitched 1 on the left. If you want to play song in D minor, it is allowed to dip under the 1 note for a bit but no more room over the next 1.</p><h2 id="the-six-practical-positions-for-major-and-minor-scales">The six practical positions for major and minor scales</h2><p>In total I discovered 6 positions each for major and minor scales. For each type, 3 are centered around lower tines and 3 centered around upper tines. Upper tines version are on slight disadvantage since the center bass tines threw some wrenches in the equation</p><p>I will give my personal suitability rating as well and sort according to it.</p><p>Start with the lower tine-based shiftings in major :</p><ul><li>Main key &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#x2B50;</li><li>Mixolydian-like (+7 Major) &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;</li><li>Lydian-like (+5 Major) &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;</li></ul><p>And minors : </p><ul><li>Aeolian (Relative minor key, +9 Minor) &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;</li><li>Phrygian-like (+4 Minor) &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;</li><li>Dorian-like (+2 Minor) &#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;</li></ul><p>Then upper tines-based starts from 3 stars. The reason because middle upper tines are occupied by the basses and needs to be avoided most of the time unless stars really aligned. Plus when you play you need to be more careful not to accidentally hit the lower one.</p><ul><li>+1 Major &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</li><li>+8 Major &#x2B50;&#xFE0F;&#x2B50;</li><li>+6 Major &#x2B50;</li></ul><p>Minors : </p><ul><li>+10 Minor &#x2B50;&#xFE0F;&#x2B50;&#xFE0F;&#x2B50;&#xFE0F;</li><li>+5 Minor &#x2B50;&#xFE0F;&#x2B50;</li><li>+3 Minor &#x2B50;</li></ul><p>This is the legend of stars : </p><ul><li>&#x2B50;&#x2B50;&#x2B50;&#x2B50;&#x2B50;&#x2B50; : Perfect, everything playable on lower tines.</li><li>&#x2B50;&#x2B50;&#x2B50;&#x2B50;&#x2B50; : One note on upper tines required to complete.</li><li>&#x2B50;&#x2B50;&#x2B50;&#x2B50; : Still centered on lower tines, but more notes on the upper.</li><li>&#x2B50;&#x2B50;&#x2B50; : Centered around upper tines. Few notes required on the lower.</li><li>&#x2B50;&#x2B50; : &#xFE0F;Slightly harder than 3 stars. More notes needed on the lower.</li><li>&#x2B50; : &#xFE0F;Yet harder than 2 stars. Probably the hardest that is still practical.</li></ul><p>Because each star has major and minor, C major Kalimba can play practically in 12 keys out of all common 24 keys (major &amp; minor *12)! Though with varying difficulties, but thats something to practice on right? It&apos;s a skill ceiling!</p><p>Why don&apos;t we try making a table assuming the Kalimba is in other key, what keys could it practically play? Seeds Instrument 34-key Pisces is available in C and B. But just in case other companies follow suit with more keys.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-6.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="578" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-6.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-6.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image-6.png 1600w, https://gametorrahod.com/content/images/2021/09/image-6.png 2346w" sizes="(min-width: 720px) 720px"></figure><p>Or if sorted by major/minor keys :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-7.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="577" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-7.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-7.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image-7.png 1600w, https://gametorrahod.com/content/images/2021/09/image-7.png 2342w" sizes="(min-width: 720px) 720px"></figure><p>The table also reveals that the upper tine positions (3 stars difficulty and lower) are all sharpened of lower tines. (e.g. C G F -&gt; C# G# F#, A E D -&gt; A# F D#) This is because one semitone higher is an effective way to &quot;blacken&quot; everything in piano keys perspective, shifting the correct notes all up.</p><p>Remembering the &quot;plus&quot; from main key will be difficult, so for me personally I would like to rename them based on those stars I gave, inspired by Harmonica&apos;s &quot;straight harp&quot;, &quot;cross harp&quot;, double-cross harp&quot; positions.</p><p>This is my personal &quot;Sirawat&apos;s 34-key Kalimba positions&quot;. Naming format is by tines row which it is mainly on (upper or lower) and ordered by playability (1, 2, or 3) and finally is it a major or minor.</p><ul><li>&#x2B50;&#x2B50;&#x2B50;&#x2B50;&#x2B50;&#x2B50; : 1st lower major/minor position. (1LP Major/Minor)</li><li>&#x2B50;&#x2B50;&#x2B50;&#x2B50;&#x2B50; : 2nd lower major/minor position. (2LP Major/Minor)</li><li>&#x2B50;&#x2B50;&#x2B50;&#x2B50; : 3rd lower major/minor position. (3LP Major/Minor)</li><li>&#x2B50;&#x2B50;&#x2B50; : 1st upper major/minor position. (1UP Major/Minor)</li><li>&#x2B50;&#x2B50; : &#xFE0F;2nd upper major/minor position. (2UP Major/Minor)</li><li>&#x2B50; : &#xFE0F;3rd upper major/minor position. (3UP Major/Minor)</li></ul><p>Replacing the previous table, we get : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-13.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="500" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-13.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-13.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image-13.png 1600w, https://gametorrahod.com/content/images/2021/09/image-13.png 2344w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-12.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="503" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-12.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-12.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image-12.png 1600w, https://gametorrahod.com/content/images/2021/09/image-12.png 2346w" sizes="(min-width: 720px) 720px"></figure><p>To make good use of these stars on my practice with my C key 34-key Kalimba, I of course should look to play C major and A minor key songs on mainly lower tines first. (Or transpose other songs to C major or A minor)</p><p>In addition, G major and E minor will be great and satisfying to pull of, and F major and D minor will be more challenging. Once I am good with that, it is time to explore 6 more upper tine-based positions once again.</p><p>Not sure if this result was intended by Seeds Pisces designer or not. But yeah, you created a monster! (That&apos;s a compliment)</p><p>That table&apos;s CSV was assembled from strings printed from Unity too. Thanks Unity!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-3.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1912" height="530" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-3.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-3.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image-3.png 1600w, https://gametorrahod.com/content/images/2021/09/image-3.png 1912w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-4.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="1034" height="664" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-4.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-4.png 1000w, https://gametorrahod.com/content/images/2021/09/image-4.png 1034w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/09/image-5.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="2000" height="1053" srcset="https://gametorrahod.com/content/images/size/w600/2021/09/image-5.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/09/image-5.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/09/image-5.png 1600w, https://gametorrahod.com/content/images/size/w2400/2021/09/image-5.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Just in case you want to do something with it : </p><pre><code>C,A,G,E,F,D,C#/Db,A#/Bb,G#/Ab,F,F#/Gb,D#/Eb
C#/Db,A#/Bb,G#/Ab,F,F#/Gb,D#/Eb,D,B,A,F#/Gb,G,E
D,B,A,F#/Gb,G,E,D#/Eb,C,A#/Bb,G,G#/Ab,F
D#/Eb,C,A#/Bb,G,G#/Ab,F,E,C#/Db,B,G#/Ab,A,F#/Gb
E,C#/Db,B,G#/Ab,A,F#/Gb,F,D,C,A,A#/Bb,G
F,D,C,A,A#/Bb,G,F#/Gb,D#/Eb,C#/Db,A#/Bb,B,G#/Ab
F#/Gb,D#/Eb,C#/Db,A#/Bb,B,G#/Ab,G,E,D,B,C,A
G,E,D,B,C,A,G#/Ab,F,D#/Eb,C,C#/Db,A#/Bb
G#/Ab,F,D#/Eb,C,C#/Db,A#/Bb,A,F#/Gb,E,C#/Db,D,B
A,F#/Gb,E,C#/Db,D,B,A#/Bb,G,F,D,D#/Eb,C
A#/Bb,G,F,D,D#/Eb,C,B,G#/Ab,F#/Gb,D#/Eb,E,C#/Db
B,G#/Ab,F#/Gb,D#/Eb,E,C#/Db,C,A,G,E,F,D</code></pre><p>You can use these stars rating effectively like this : if you want to jam together with others perhaps while drinking with an acoustic guitar, a common problem is that they don&apos;t want to transpose the song just for you to be able to play on 1st position, but transpose down a bit from the original key mainly so the singer is more comfortable. (We are chilling so we don&apos;t want to get too high, and inexperienced singer that can&apos;t get high could join.) Guitar players can use a capo, but Kalimba player can&apos;t use a tuning hammer that fast. So you can remember this if you own a C Kalimba : C G F C# G# F# (major), or A E D A# F D# (minor) (all the playing positions in ascending star difficulty order) and ask for a compromise if we could get the key to be one of these.</p><h3 id="diagrams-recap-lower-tine-positions-">Diagrams recap, lower tine positions :</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-1.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="958" height="534" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-1.png 600w, https://gametorrahod.com/content/images/2021/08/image-1.png 958w" sizes="(min-width: 720px) 720px"><figcaption>1LP Major</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-10.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="950" height="442" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-10.png 600w, https://gametorrahod.com/content/images/2021/08/image-10.png 950w" sizes="(min-width: 720px) 720px"><figcaption>2LP Major</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-11.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="946" height="428" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-11.png 600w, https://gametorrahod.com/content/images/2021/08/image-11.png 946w" sizes="(min-width: 720px) 720px"><figcaption>3LP Major</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-19.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="946" height="418" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-19.png 600w, https://gametorrahod.com/content/images/2021/08/image-19.png 946w" sizes="(min-width: 720px) 720px"><figcaption>1LP Minor</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-13.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="948" height="436" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-13.png 600w, https://gametorrahod.com/content/images/2021/08/image-13.png 948w" sizes="(min-width: 720px) 720px"><figcaption>2LP Minor</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-12.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="948" height="436" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-12.png 600w, https://gametorrahod.com/content/images/2021/08/image-12.png 948w" sizes="(min-width: 720px) 720px"><figcaption>3LP Minor</figcaption></figure><h3 id="diagrams-recap-upper-tine-positions-">Diagrams recap, upper tine positions :</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-14.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="952" height="444" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-14.png 600w, https://gametorrahod.com/content/images/2021/08/image-14.png 952w" sizes="(min-width: 720px) 720px"><figcaption>1UP Major</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-18.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="944" height="420" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-18.png 600w, https://gametorrahod.com/content/images/2021/08/image-18.png 944w" sizes="(min-width: 720px) 720px"><figcaption>2UP Major</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-17.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="952" height="436" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-17.png 600w, https://gametorrahod.com/content/images/2021/08/image-17.png 952w" sizes="(min-width: 720px) 720px"><figcaption>3UP Major</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-20.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="942" height="430" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-20.png 600w, https://gametorrahod.com/content/images/2021/08/image-20.png 942w" sizes="(min-width: 720px) 720px"><figcaption>1UP Minor</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-16.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="952" height="432" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-16.png 600w, https://gametorrahod.com/content/images/2021/08/image-16.png 952w" sizes="(min-width: 720px) 720px"><figcaption>2UP Minor</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/08/image-15.png" class="kg-image" alt="34-key Kalimba music theory analysis" loading="lazy" width="948" height="442" srcset="https://gametorrahod.com/content/images/size/w600/2021/08/image-15.png 600w, https://gametorrahod.com/content/images/2021/08/image-15.png 948w" sizes="(min-width: 720px) 720px"><figcaption>3UP Minor</figcaption></figure><h3 id="new-chord-possibilities">New chord possibilities</h3><p>With all the scales explored, also we have more ways to play chords that has out-of-key component.</p><p>For example, any time you play a major chord and its middle component lands on upper tine, you can move that down to instantly transform it to minor chord! And vice versa, any minor chord on lower tines can be turned into major by moving the middle component up. Quite good amount of songs unexpectedly play the major-minor inverted version of the chord to defy expectation of listener, we can now cover that with this 34-key Kalimba.</p><p>Other exotic chords may appear accidentally, like augmented or suspended chords that requires some notes closer to each other. I have not look closely at these yet but it will be interesting to play these sounds.</p><h2 id="conclusion">Conclusion</h2><p>34-key arrangement on Seeds Instruments Pisces adds tons of entangled possibilities, just like how 6 open guitar strings gave birth to million of both practical and impractical chord shapes. If we figured out music theory behind a particular &quot;system&quot; produced by the instrument, chances are we will have a lot of fun stuff we could practice on! Overall, I am content with my 34-key Kalimba purchase and I will try to get better on it.</p><p>Ps. They made a new monster again called the Columbus. 24-key, but in ascending order, diatonic, allowing complete L-R hand multitasking like the piano. Some sick constant chords running while melody doing its own thing on the right side is now easier. Glissando always go from left to right. The most important appeal for me is that it goes <strong>high, </strong>a blessing for minor key music lover like me since when you play in A minor (relative minor of C major that most Kalimba came with), the upper range gets eaten up by the shift and make emotional part of the song fell off the edge.. This also looks fun! But I don&apos;t have any more spare cash to get another.. for now. &#x1F602; Maybe, I will be back with an another analysis on the Columbus in the future!</p><figure class="kg-card kg-embed-card"><iframe width="356" height="200" src="https://www.youtube.com/embed/k7zb-iyQD_I?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure>]]></content:encoded></item><item><title><![CDATA["Tibetan Breaking" Specification]]></title><description><![CDATA[Tibetan Breaking is a convention to encode line breaking opportunity inside the string data itself, using a Tibetan interpunct ⟨་⟩ character, called ཙེག་ (tsek).]]></description><link>https://gametorrahod.com/tibetan-breaking-specification/</link><guid isPermaLink="false">623c5d7df445b7063911bf50</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Thu, 18 Feb 2021 04:47:40 GMT</pubDate><media:content url="https://gametorrahod.com/content/images/2021/02/Screen-Shot-2021-02-18-at-11.35.55.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/02/image-1.png" class="kg-image" alt="&quot;Tibetan Breaking&quot; Specification" loading="lazy" width="1970" height="882" srcset="https://gametorrahod.com/content/images/size/w600/2021/02/image-1.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/02/image-1.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/02/image-1.png 1600w, https://gametorrahod.com/content/images/2021/02/image-1.png 1970w" sizes="(min-width: 720px) 720px"></figure><img src="https://gametorrahod.com/content/images/2021/02/Screen-Shot-2021-02-18-at-11.35.55.png" alt="&quot;Tibetan Breaking&quot; Specification"><p>Tibetan Breaking is a convention to encode <strong>line breaking opportunities</strong> inside the string data itself, using a <a href="https://en.wikipedia.org/wiki/Standard_Tibetan">Tibetan</a> interpunct &#x27E8;&#xF0B;&#x27E9; character, called &#xF59;&#xF7A;&#xF42;&#xF0B; (<em>tsek</em>). (<a href="https://en.wikipedia.org/wiki/Tibetan_script">Tibetan Script</a>) From now on I will also call &#x27E8;&#xF0B;&#x27E9; the &quot;Tsek&quot;.</p><p>All strings that does not have white space or similar to hint line breaking should have Tsek and parser should be prepared to remove them from display if they are not intended to print them out, or even better, converts them into real line breaking opportunities that enhances reading experience.</p><p>Note that line breaking <strong>opportunity </strong>does not mean it must break at that position like <code>&lt;br&gt;</code>, but it <strong>could.</strong> Presence of this character <strong>also</strong> means it should not break at any other places between characters, save for some punctuations that has language-specific rules.</p><h2 id="example">Example</h2><ul><li><code>&#xE17;&#xE38;&#xE01;&#xE27;&#xE31;&#xE19;&#xE19;&#xE35;&#xE49;&#xF0B;&#xE08;&#xE48;&#xE32;&#xE22;&#xE20;&#xE32;&#xE29;&#xE35;&#xF0B;&#xE44;&#xE1B;&#xE40;&#xE17;&#xE48;&#xE32;&#xE44;&#xE2B;&#xE23;&#xE48;&#xF0B;&#xE01;&#xE47;&#xE44;&#xE21;&#xE48;&#xE40;&#xE2B;&#xE47;&#xE19;&#xE27;&#xE48;&#xE32;&#xF0B;&#xE1B;&#xE23;&#xE30;&#xE40;&#xE17;&#xE28;&#xE0A;&#xE32;&#xE15;&#xE34;&#xF0B;&#xE08;&#xE30;&#xE1E;&#xE31;&#xE12;&#xE19;&#xE32;&#xF0B;&#xE40;&#xE17;&#xE48;&#xE32;&#xE17;&#xE35;&#xE48;&#xE08;&#xE48;&#xE32;&#xE22;&#xE44;&#xE1B;&#xE40;&#xE25;&#xE22;</code></li><li><code>&#x8D64;&#x3044;&#xF0B;&#x7532;&#x6BBB;&#x306B;&#xF0B;&#x8EAB;&#x3092;&#xF0B;&#x5305;&#x3080;&#x3001;&#x706B;&#x7ADC;&#x3068;&#x3082;&#xF0B;&#x547C;&#x3070;&#x308C;&#x308B;&#xF0B;&#x98DB;&#x7ADC;&#xF0B;&#x30EA;&#x30AA;&#x30B9;&#x306E;&#x96C4;&#x3002;</code></li></ul><h2 id="why-should-we-use-this-character">Why should we use this character</h2><ul><li>It is inlined with the text. Line breaking opportunities being a single entity (as opposed to range surrounders like <code>**</code> in Markdown), can be encoded as an accompanying array of integer offsets to the text, so the &quot;clean&quot; text is perfectly readable and editable. But it is now difficult to update the array after modifying the text. Having an actual character in the string is better. Also pairing different data to the string might be difficult to manage.</li><li>Almost invisible when you don&apos;t want to see, but at the same time visible when you want to edit them. This is better than thin character like a pipe <code>|</code> where it may not take much space, it is obtrusive to read. And better than a completely invisible character like the <a href="https://www.compart.com/en/unicode/U+2028">Line Separator</a> (Sometimes that even show up as a tofu box.) where it is difficult to notice that you are editing over them. (e.g. Pressing backspace and nothing happen, so that means you are deleting this invisible character.)</li><li><a href="https://www.compart.com/en/unicode/U+00B7">Middle dot</a> <code>&#xB7;</code> character is almost as good, but Tsek has an advantage that it is usually not present in most monospace fonts. It then falls back to a font that is not monospace, making it ideal in code editor because it stays small.</li><li>In online Localization collaboration platform, you don&apos;t need any special features to manage the line breaking opportunities. You may add a tag that Tsek is present to save processing cost for strings that does not have them at the destination.</li></ul><h2 id="disadvantages">Disadvantages</h2><ul><li>You likely cannot type from keyboard, but having to copy paste it.</li><li>If you are making a string of an actual Tibetan language, then the parser needs to know to print them literally rather than process them...</li></ul><h2 id="relationship-with-other-markup-languages">Relationship with other markup languages</h2><p><a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>&apos;s success came from it using a very humane markup format. We can both encode different highlighting that make different on the destination media, and also can read it aloud and feels like it has the same &quot;highlighting&quot; effect.</p><p>Tibetan Breaking is the same as Markdown that we are directly encoding more information inside the text. However for presentation, line breaking is naturally invisible so it make sense that this encoded information is the most invisible as possible when we read it in plain text editor. You can have Tibetan Breaking and Markdown together.</p><p>Tibetan Breaking is different that it use a single character Tsek, not surrounders like most of Markdown&apos;s syntax.</p><p>HTML-like &quot;enriched text&quot; format can sometimes encode a single entity (e.g. <code>&lt;br&gt;</code>) or surrounders (e.g. <code>&lt;strong&gt;asdf&lt;/strong&gt;</code>) but neither are pleasant to read.</p><h2 id="the-need-for-explicit-line-breaking-opportunity">The need for explicit line breaking opportunity</h2><p><a href="https://github.com/google/budou">Google&apos;s Budou</a> documentation explained this very nicely.</p><!--kg-card-begin: markdown--><blockquote>
<p>English text has many clues, like spacing and hyphenation, that enable beautiful and legible line breaks. Some CJK languages lack these clues, and so are notoriously more difficult to process. Without a more careful approach, breaks can occur randomly and usually in the middle of a word. This is a long-standing issue with typography on the web and results in a degradation of readability.</p>
</blockquote>
<!--kg-card-end: markdown--><p>In many places like website, text processor, game engine, etc. the &quot;breakable at white space but not in between other characters&quot; are <strong>hard coded</strong>. In east Asian characters, there are <a href="https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages">more characters</a> hard coded to be breakable. They can also be limited to not be able to use at the start/end of line, so it works together with line breaking code.</p><p>One more hard-coded behaviour is that if CJK language is found, suddenly it is breakable at <strong>all </strong>characters. You absolutely don&apos;t want to allow this for English. But in CJK if you don&apos;t allow this, strings of character without any hints of space can easily exceed the line. They can&apos;t break intelligently semantically like human do either, so they must settle for this simple but not optimal break at all characters.</p><p>Other than this, many languages wants to break mid-strings but of course <strong>not anywhere</strong>. In Japanese, a string of kanjis can be broken into words with different meaning than intended, sometimes even 1 kanji is readable. In Thai, there are more chance to create a word that is readable cross-line but is not optimal as there is a long pause before your eye scan to the beginning of the next line.</p><h3 id="critical-for-responsive-media">Critical for responsive media</h3><p>Unlike physical media like signs, strings can used in dynamic media. Line breaking tied with the width of that media. Nowadays websites could be viewed on mobile device. Even a smartphone games can be responsive to the device&apos;s screen and provide longer or shorter line depending on device, or even orientation you play on.</p><p>Here is an example from Budou&apos;s project page. &#x30E2;&#x30D0;&#x30A4;&#x30EB; (Mobile) and &#x30C1;&#x30FC;&#x30E0; (Team) should absolutely not be broken, but the algorithm is set to &quot;breakable at every character&quot; by default for Asian language. At the same time, we can&apos;t sneak in white spaces in Japanese language to hint the browser. Without manually marking line breaking <strong>opportunity </strong>you don&apos;t know at which width it will gives the reader sub-optimal experience.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gametorrahod.com/content/images/2021/02/nexus_example.jpg" class="kg-image" alt="&quot;Tibetan Breaking&quot; Specification" loading="lazy" width="1500" height="763" srcset="https://gametorrahod.com/content/images/size/w600/2021/02/nexus_example.jpg 600w, https://gametorrahod.com/content/images/size/w1000/2021/02/nexus_example.jpg 1000w, https://gametorrahod.com/content/images/2021/02/nexus_example.jpg 1500w" sizes="(min-width: 720px) 720px"><figcaption><a href="https://github.com/google/budou">google/budou: Budou is an automatic organizer tool for beautiful line breaking in CJK (Chinese, Japanese, and Korean). (github.com)</a></figcaption></figure><p>Even the text above of this very blog broke in an unfortunate place at a certain screen size! Look at that unfortunate &#xA0;<code>&#x30FC;&#x30E0;</code> at new line that is not even readable...</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/02/image-3.png" class="kg-image" alt="&quot;Tibetan Breaking&quot; Specification" loading="lazy" width="1628" height="980" srcset="https://gametorrahod.com/content/images/size/w600/2021/02/image-3.png 600w, https://gametorrahod.com/content/images/size/w1000/2021/02/image-3.png 1000w, https://gametorrahod.com/content/images/size/w1600/2021/02/image-3.png 1600w, https://gametorrahod.com/content/images/2021/02/image-3.png 1628w" sizes="(min-width: 720px) 720px"></figure><p>Here is an example from Unity game engine, the default behavior which is the same as website. Each character is completely independent and breakable. Imagine the changing boundary as various phone size.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/02/before.gif" class="kg-image" alt="&quot;Tibetan Breaking&quot; Specification" loading="lazy" width="1153" height="514"></figure><p>An ideal result : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2021/02/jp.gif" class="kg-image" alt="&quot;Tibetan Breaking&quot; Specification" loading="lazy" width="1153" height="514"></figure><h3 id="technical-fixes-is-possible-after-having-tsek">Technical fixes is possible after having Tsek</h3><p>This goes a bit beyond the purpose of this article but just for completeness. Supposed we had encoded Tsek in the text. How to decode them to use in the destination media?</p><p>On website, if you use Budou&apos;s way you can wrap each &quot;chunk&quot; in <code>&lt;span&gt;</code> with a class <code>display: inline-block;</code>, then all chunks inside an another empty <code>&lt;span&gt;</code>. (Though, Budou tries to use machine learning to automatically place breaking opportunity. Tsek has real human place them manually.)</p><p>An another way is to have the parent with <code><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/white-space">white-space</a>: nowrap</code> to cancel out the built-in &quot;break anywhere&quot; of Asian language then place <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr">&lt;wbr&gt;</a></code> inside the HTML. Be careful if you use <code>white-space: no-wrap</code> with English it will cancel out the already great white space breaking. So you need to be selective of languages. With this convention, you can check existence of Tsek character to activate <code>white-space: nowrap</code> without knowing what language it is.</p><p>Also, <code>&lt;wbr&gt;</code> simplify processing since you just replace Tsek with it. If you use <code>&lt;span&gt;</code> wrapping, it could be a problem when you have Tsek in the middle of interleaving surrounders that also needs to be processed.</p><p>In other system like game engine that supports &quot;rich text&quot; like Unity, you may wrap each chunk in something like <code>&lt;nobr&gt;...&lt;/nobr&gt;</code> (non-breaking text) to counter the breakable anywhere rule of CJK language. Then in-between, ensure it is breakable with <code>&lt;zwsp&gt;</code> (zero-width space) to silently add hints to CJK language which has no hint before.</p><p>In summary, all these technical fixes while usable, are absolutely not ideal to be encoded directly in the text as they are horrible to read and edit, and interferes with good encoding like Markdown.</p>]]></content:encoded></item><item><title><![CDATA[Dumb i18n site]]></title><description><![CDATA[I don't know how to call this sort of internationalization scheme on a website so I coined it for my own use. Explaining this gets repetitive so I made this article to link to every time I wanted to talk about it.]]></description><link>https://gametorrahod.com/dumb-i18n-site/</link><guid isPermaLink="false">623c5d7df445b7063911bf4f</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Wed, 17 Feb 2021 05:39:14 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1593234270607-66cc705a0aaa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMTc3M3wwfDF8c2VhcmNofDN8fGR1bWJ8ZW58MHx8fA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1593234270607-66cc705a0aaa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMTc3M3wwfDF8c2VhcmNofDN8fGR1bWJ8ZW58MHx8fA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Dumb i18n site"><p>I don&apos;t know how to call this sort of internationalization scheme on a website so I coined it for my own use. Explaining this gets repetitive so I made this article to link to every time I wanted to talk about it.</p><p>What is this kind of article doing in a blog about game dev? This is my favorite kind of site to use for game&apos;s home page! It should maximize accessibility as it is common that you publish the game to multiple countries.</p><p>It has these characteristics : </p><h2 id="no-question-asked-url-to-choose-the-language">No question asked, URL to choose the language</h2><p>It is a site that use purely URL to choose which language to display. (e.g. <code>example.com/th/about</code> to display Thai version.) No cookies, no IP address detection can triumph over it.</p><p>Each language of each page exists for real as a <strong>separated</strong> HTML file. (But of course computer should do this for you, not manually.)</p><h3 id="direct-edits-ok">Direct edits OK</h3><p>User can directly edit URL to go to a different language if they think that language code exists. This is ineffective on some sites that are &quot;not dumb enough&quot; and insist on displaying a certain language depending on your location, your browser, some settings deep in the cog wheel menu, or your cookie. And usually they will not tell you how the decision works. (I think this is frustrating rather than convenience.)</p><h3 id="unsupported-languages">Unsupported languages</h3><p>Unsupported language returns not found error (e.g. <code>example.com/asdf/about</code>) rather than a random redirection to default language. If all your language pages are really a separated HTML files, this should happen automatically.</p><h2 id="honest-a-links">Honest <code>&lt;a&gt;</code> links</h2><p>All <code>&lt;a&gt;</code> links inside a specific language should contain the destination of that language for real. e.g. <code>example.com/th/about</code> has a link to go to <code>example.com/th/contact</code> but not a &quot;neutral&quot; &#xA0;<code>example.com/contact</code> that you may need to rely on other tricks to finally come back to the right langauge. </p><p>When user hovers the mouse, they should see that clearly appearing on the browser&apos;s corner.</p><p>We make the crawler life as easiest as possible.</p><h2 id="neutral-page">Neutral page</h2><p>If user type &quot;neutral URL&quot; (no language slug) of any page, that page exists for real as an HTML file as well. The language is of a fixed default language (e.g <code>en</code>). Therefore it is a duplicated file of one of the language you supported. Again, machine should perform this duplication on building the website, not you.</p><p>Those neutral URL may use rewrite technique (serve same content, URL unchanged) according to something like <a href="https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language">navigator&apos;s language</a> or visitor&apos;s IP address to instead show a content from language specific page, while not touching the URL. If rewriting fails (no specific page supported), then show the neutral HTML as a fallback.</p><p>Note that with rewrites instead of redirection, user can copy the URL and share it without making their own country appears in the URL if they just visited the page and hadn&apos;t navigate anywhere else yet.</p><p>But as we use rewrites, all the links in that page will go to language specific page according to &quot;honest <code>&lt;a&gt;</code> links&quot; rule. Navigating once from the rewritten page will turn back into &quot;honest&quot; version with language in the URL.</p><p>The neutral page allows putting your homepage URL without country code on a printed media and use it (e.g. a poster) in multiple geolocations.</p><p>Also, search engine crawlers may appears to come from multiple countries. If you do this right, the neutral URL will present different content to the crawler. This is great, as Japanese user coming from the search engine will see Japanese content, but the URL is <code>example.com</code> and not <code>example.com/jp</code>. (Though any navigation performed next will adds <code>jp</code>)</p><ul><li>Some static hosting site has some config exposed to help you do this. For example Firebase Hosting has <a href="https://firebase.google.com/docs/hosting/i18n-rewrites">i18n rewrites</a>.</li></ul><h2 id="rich-metadata-crawlers-are-welcomed">Rich metadata, crawlers are welcomed</h2><p>Rely on search engine so user of a specific country get to the page on the first time with the right language.</p><h3 id="it-knows-all-its-alter-ego">It knows all its alter-ego</h3><p>Each page has <code>&lt;link rel=&quot;alternate&quot; hreflang=&quot;x-default&quot; href=&quot;HOST/NEUTRAL_PATH&quot; /&gt;</code> and all other <code>&lt;link rel=&quot;alternate&quot; hreflang=&quot;LANGUAGE&quot; href=&quot;HOST/LANGUAGE/NEUTRAL_PATH&quot; /&gt;</code> so all pages knows each other.</p><p>Each page has <code>&lt;meta property=&quot;og:url&quot; content=&quot;REAL_HOST/LANGUAGE/NEUTRAL_PATH&quot; /&gt;</code> of only its own, with the right language code. This make it works with Facebook and Twitter.</p><p><a href="https://developers.google.com/search/docs/advanced/crawling/localized-versions">Read more about it here</a>. Use the HTML tags way.</p><h3 id="localized-head-content">Localized <code>&lt;head&gt;</code> content</h3><ul><li>Localized <code>&lt;meta name=&quot;keywords&quot; content=&quot;keyword1, keyword2, ...&quot;/&gt;</code>.</li><li>Localized <code>&lt;title&gt;</code> and <code>&lt;meta property=&quot;og:title&quot; content=&quot;...&quot;/&gt;</code> and <code>&lt;meta name=&quot;twitter:title&quot; content=&quot;...&quot;/&gt;</code> (max 70 characters for Twitter).</li><li>Localized <code>&lt;meta name=&quot;description&quot; content=&quot;...&quot;/&gt;</code> (for Google, search engines), <code>&lt;meta property=&quot;og:description&quot; content=&quot;...&quot;&gt;</code> (Open Graph, Facebook) and <code>&lt;meta name=&quot;twitter:description&quot; content=&quot;...&quot;/&gt;</code> (max 200 characters for Twitter).</li><li>Localized social image, for example if they contain a lot of text or your product logo if you localized it.</li><li>The Open Graph image <code>&lt;meta property=&quot;og:image&quot; content=&quot;...&quot;/&gt;</code></li><li>The Twitter image <code>&lt;meta name=&quot;twitter:image&quot; content=&quot;...&quot;/&gt;</code></li><li>Alt image description for the visually impaired : <code>&lt;meta property=&quot;og:image:alt&quot; content=&quot;...&quot; /&gt;</code> and <code>&lt;meta name=&quot;twitter:image:alt&quot; content=&quot;...&quot; /&gt;</code>.</li><li>If your page is for an app, localized <code>&lt;meta name=&quot;twitter:app:name:iphone&quot; content={appName} /&gt;</code>, <code>&lt;meta name=&quot;twitter:app:name:ipad&quot; content={appName} /&gt;</code>, and <code>&lt;meta name=&quot;twitter:app:name:googleplay&quot; content={appName} /&gt;</code> for use with app-type card on Twitter.</li></ul><h3 id="what-about-html-lang-">What about <code>&lt;html lang=&quot;&quot;&gt;</code>?</h3><p>Actually <strong>all tags</strong> are able to accept <code>lang</code> attribute. This would be ideal if all of your <code>&lt;div&gt;</code> soup has <code>lang</code> but I think it would be a major pain. (<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang">MDN</a>)</p><p>So how about just put the right <code>lang=&quot; &quot;</code> inside <code>&lt;html&gt;</code>? It maybe a good idea but note that Google recommends nothing about it <a href="https://developers.google.com/search/docs/advanced/crawling/localized-versions">in this page</a>. Therefore I think this is not an issue if you can&apos;t do it from your framework.</p><h2 id="honest-language-switcher">Honest language switcher</h2><p>If there is no dedicated &quot;switch language page&quot; and each page has a dropdown, modal or a list to select all other versions, all those are plain <code>&lt;a&gt;</code> link that goes to the <strong>same page</strong> but with different language. (e.g. Clicking on &quot;JAPAN&quot; while on <code>example.com/th/about</code> links you to <code>example.com/jp/about</code>, not resetting to <code>example.com/jp</code>.)</p><p>That is an <strong>inverse </strong>of honest <code>&lt;a&gt;</code> links, where it preserves the language but go to a different page.</p><p>That means if you go to a different page, the component for switching language which may just looked the same as in previous page, actually has all its link adjusted.</p><p>Again! This might sounds painful to do it manually but you should use the power of modern web programming to do it for you in your front end framework of your choice.</p><h2 id="only-just-enough-strings-sent-to-client-if-possible">Only just enough strings sent to client if possible</h2><p>You might be using i18n packages that could accept an object kinda this shape :</p><pre><code class="language-json">{
  &quot;en&quot;: {
    &quot;term1&quot;: &quot;value1&quot;,
    &quot;term2&quot;: &quot;value2&quot;
  },
  &quot;th&quot;: {
    &quot;term1&quot;: &quot;value1&quot;,
    &quot;term2&quot;: &quot;value2&quot;
  },
  &quot;jp&quot;: {
    &quot;term1&quot;: &quot;value1&quot;,
    &quot;term2&quot;: &quot;value2&quot;
  }
}</code></pre><p>Then using &quot;current language&quot; switch, any key request (e.g. <code>format(&quot;term1&quot;)</code>) will get the right string.</p><p>If your site is not hydrated and any &#xA0;<code>&lt;a&gt;</code> link traversal is really going to a new page, the HTML can contain the most minimal strings needed for that one page in that one language.</p><p>If you need interactivity, for example the site hydrates and changing page now swaps DOM instead, then try to lazily add more strings. Because now if you consider switching language a hydrated action also, you will need strings of all pages of all languages ready. Popular solution is to preload more only when user clicks the link or even when hovering over the link.</p><p>Strings are usually small, so I think this rule is not that critical. I doubt even having all strings, of all pages, of all languages at the first load is even that costly for most site.</p><h2 id="example">Example</h2><p>Anyways, I have been trying to make a site of my own game to follow this pattern as much as possible : <a href="https://duelotters.com/">https://duelotters.com/</a>. You can take a look for example, or even view the HTML source if each language is really dumb or not.</p><p>Note that the source looks alien since it is built with Svelte + Sapper, and the code in there is designed to perform direct DOM edit as is the characteristic of Svelte. (As opposed to DOM-diffing with React or Vue.) You need to figure out on your tooling stack of choice how to achieve the &quot;dumb&quot; status. Next.JS, for example, already has <a href="https://nextjs.org/docs/advanced-features/i18n-routing">i18n routing</a> which should help you work out.</p><p>And that&apos;s all! Maybe it doesn&apos;t sounds &quot;dumb&quot; after you see all the repetitive but-slightly-different HTML files needed for each page, but it is in a sense that it works rather in a straightforward way from visitor point of view. You are the site&apos;s developer should take the pain... luckily modern site building tools are great.</p>]]></content:encoded></item><item><title><![CDATA[EventSystem raycast debugging guide]]></title><description><![CDATA[Using EventSystem along with GraphicRaycaster, PhysicsRaycaster, or Physics2DRaycaster, and something didn't hit the way you expect it to? Let's dive into the code to debug this.]]></description><link>https://gametorrahod.com/eventsystem-raycast-debugging-guide/</link><guid isPermaLink="false">623c5d7df445b7063911bf4d</guid><category><![CDATA[Unity]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Thu, 23 Jul 2020 08:46:06 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1530224264768-7ff8c1789d79?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1530224264768-7ff8c1789d79?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="EventSystem raycast debugging guide"><p>Using <code>EventSystem</code> along with <code>GraphicRaycaster</code>, <code>PhysicsRaycaster</code>, or <code>Physics2DRaycaster</code>, and something didn&apos;t hit the way you expect it to? Let&apos;s dive into the code to debug this.</p><p>ps. it&apos;s been a long time since the last one! I was away from game development because I got a real, totally unrelated to my passion, boring front end job that actually pays.. lol</p><h2 id="example-problem">Example problem</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="1601" height="779" srcset="https://gametorrahod.com/content/images/size/w600/2020/07/image.png 600w, https://gametorrahod.com/content/images/size/w1000/2020/07/image.png 1000w, https://gametorrahod.com/content/images/size/w1600/2020/07/image.png 1600w, https://gametorrahod.com/content/images/2020/07/image.png 1601w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image-1.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="1623" height="792" srcset="https://gametorrahod.com/content/images/size/w600/2020/07/image-1.png 600w, https://gametorrahod.com/content/images/size/w1000/2020/07/image-1.png 1000w, https://gametorrahod.com/content/images/size/w1600/2020/07/image-1.png 1600w, https://gametorrahod.com/content/images/2020/07/image-1.png 1623w" sizes="(min-width: 720px) 720px"></figure><p>A game about clicking cookie coming into the screen. They are all 2D so I used <code>Physics2DRaycaster</code>. However, when touching any other area that is not the cookie we need to play some effects at that position as well.</p><p>Therefore on each cookie <code>CircleCollider2D</code> has been attached along with <code>IPointerDownHandler</code> or <code>EventHandler</code> setup properly. And there is one more <code>FullScreenTouch</code> to handle the miss-touch. This one is a <code>BoxCollider2D</code>.</p><ul><li>I assume you know <code>EventTrigger</code> only ever trigger one candidate at a time. How that candidate be selected?</li><li>There is only one <code>EventTrigger</code> in the scene. Otherwise you get tons of warning spam. It is a hub to get all kind of raycasters in the scene.</li></ul><h2 id="what-is-an-eventsystem">What is an EventSystem ?</h2><p>If you are an old timer Unity developer, you may used to &quot;dumbly&quot; place <code>Input.</code> check in some of your <code>Update</code> and if this and that, things happen. Then left click only works in editor, so you add <code>Input.touches</code> check, etc.</p><p>Actually <code>EventSystem</code> is not far from that at all. It has <code>Update</code> rapid check that eventually leads to <code>Input</code> like I described. Except that they made it more systematic. I recommend follow into <code>EventSystem</code> code and see its <code>Update</code> for yourself.</p><ul><li>Input initiator is already coded. If left clicking works in editor, then at runtime a touch also initiate the same code.</li><li>What to do is determined by raycasting, which the <code>EventSystem</code> could collect all casters and gather candidates, and pick the best one. The candidate is further filtered by event type, such as pointer up.</li><li>You perform additional checks with a given <code>BaseEventData</code> or <code>PointerEventData</code> given on your candidates. The input&apos;s details are collected in that data. Note that if you aren&apos;t satisfied with the input coming it, it is not possible to &quot;let it go&quot; and bubble up to the next candidate. This object had already been picked by the <code>EventSystem</code>. You have to write more custom logic to do that.</li></ul><h2 id="problem">Problem</h2><p>The plan is to have the cookie take priority.</p><ul><li>However, turns out the cookie didn&apos;t get trigger at all, only the <code>FullScreenTouch</code> one.</li><li>More observation, as soon as I disable the full screen box collider the cookie get triggered. This means the cookie collider logic is correct and could trigger, but the bigger box 2d ones always get the priority.</li><li>No, moving the cookies to Z = -0.01 doesn&apos;t make them win the selection.</li></ul><p>Why?</p><h2 id="assembling-the-candidates">Assembling the candidates</h2><p>It all starts on <code>RaycastAll</code> method in <code>EventSystem</code> when you do something. You can manually invoke it! But if you use your mouse to click, then the invoker is probably <code>StandardInputModule</code>.</p><pre><code class="language-csharp">        /// &lt;summary&gt;
        /// Raycast into the scene using all configured BaseRaycasters.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;eventData&quot;&gt;Current pointer data.&lt;/param&gt;
        /// &lt;param name=&quot;raycastResults&quot;&gt;List of &apos;hits&apos; to populate.&lt;/param&gt;
        public void RaycastAll(PointerEventData eventData, List&lt;RaycastResult&gt; raycastResults)
        {
            raycastResults.Clear();
            var modules = RaycasterManager.GetRaycasters();
            for (int i = 0; i &lt; modules.Count; ++i)
            {
                var module = modules[i];
                if (module == null || !module.IsActive())
                    continue;

                module.Raycast(eventData, raycastResults);
            }

            raycastResults.Sort(s_RaycastComparer);
        }</code></pre><p>For each module, amount of candidate it could gather depends. In my case, the setting isn&apos;t wrong. I have allowed two. So it would get at least the background and one of the cookie provided that cookie can&apos;t ever overlap. 2 here is correct. And the raycaster gathered them in.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image-2.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="381" height="108"></figure><p>But the key is that <code>raycastResults.Sort</code>. <strong>Only the first one of sorted result </strong>will win and get invoked appropriate event handler.</p><p>You probably noticed your UI things always win and it didn&apos;t let the event go through anything under it. Well, things under it actually are gathered but after the sort the UI ones always win.</p><h2 id="raycast-comparer">Raycast comparer</h2><p>This is the destination for your raycast debugging needs. When something doesn&apos;t trigger as you expected.</p><pre><code class="language-csharp">private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
    if (lhs.module != rhs.module)
    {
        var lhsEventCamera = lhs.module.eventCamera;
        var rhsEventCamera = rhs.module.eventCamera;
        if (lhsEventCamera != null &amp;&amp; rhsEventCamera != null &amp;&amp; lhsEventCamera.depth != rhsEventCamera.depth)
        {
            // need to reverse the standard compareTo
            if (lhsEventCamera.depth &lt; rhsEventCamera.depth)
                return 1;
            if (lhsEventCamera.depth == rhsEventCamera.depth)
                return 0;

            return -1;
        }

        if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
            return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

        if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
            return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
    }

    if (lhs.sortingLayer != rhs.sortingLayer)
    {
        // Uses the layer value to properly compare the relative order of the layers.
        var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
        var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
        return rid.CompareTo(lid);
    }

    if (lhs.sortingOrder != rhs.sortingOrder)
        return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

    // comparing depth only makes sense if the two raycast results have the same root canvas (case 912396)
    if (lhs.depth != rhs.depth &amp;&amp; lhs.module.rootRaycaster == rhs.module.rootRaycaster)
        return rhs.depth.CompareTo(lhs.depth);

    if (lhs.distance != rhs.distance)
        return lhs.distance.CompareTo(rhs.distance);

    return lhs.index.CompareTo(rhs.index);
}
</code></pre><p>It is a series of <code>return</code>. That means this is a steps with earlier one having priority over. If we go through each one, surely we could debug anything!</p><h3 id="event-camera">Event camera</h3><p>Raycast begins from a camera. Which camera has closer to you depth, then that wins the sort.</p><h3 id="raycaster-kind-priority">Raycaster kind priority</h3><p>Then <code>sortOrderPriority</code> and <code>renderOrderPriority</code> which depends on raycaster. This is why <code>GraphicRaycaster</code> for UI always win the sort. But my problem is the battle among <code>Physics2DRaycaster</code>. Let&apos;s move to the next one.</p><h3 id="sorting-layer-and-sorting-order">Sorting layer and sorting order</h3><p>All <code>GameObject</code> has a <strong>layer</strong>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image-3.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="413" height="98"></figure><p>That could be used on the raycaster so candidates are further reduced. You can combine multiple layers into a mask.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image-2.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="381" height="108"></figure><p>However &quot;sorting layer&quot; here is not that. It is an event system specific value which is :</p><pre><code class="language-csharp">        /// &lt;summary&gt;
        /// The SortingLayer of the hit object.
        /// &lt;/summary&gt;
        /// &lt;remarks&gt;
        /// For UI.Graphic elements this will be the values from that graphic&apos;s Canvas
        /// For 3D objects this will always be 0.
        /// For 2D objects if a SpriteRenderer is attached to the same object as the hit collider that SpriteRenderer sortingLayerID will be used.
        /// &lt;/remarks&gt;</code></pre><pre><code class="language-csharp">        /// &lt;summary&gt;
        /// The SortingOrder for the hit object.
        /// &lt;/summary&gt;
        /// &lt;remarks&gt;
        /// For Graphic elements this will be the values from that graphics Canvas
        /// For 3D objects this will always be 0.
        /// For 2D objects if a SpriteRenderer is attached to the same object as the hit collider that SpriteRenderer sortingOrder will be used.
        /// &lt;/remarks&gt;</code></pre><p>Now I could see why I am having a problem right now. The wording may not 100% suggest this but I have look at other area in the source code : to qualify for &quot;3D object&quot; you need <code>SpriteRenderer</code> attached. Otherwise your layer/order is 0.</p><p>It happen that I thought my invisible full screen box collider didn&apos;t need any graphic so I didn&apos;t add <code>SpriteRenderer</code>.</p><p>My cookies surely are otherwise you can&apos;t see them. Let&apos;s look at the layer first. (Because the order tied at 0) They are <code>Important</code>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image-4.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="727" height="507" srcset="https://gametorrahod.com/content/images/size/w600/2020/07/image-4.png 600w, https://gametorrahod.com/content/images/2020/07/image-4.png 727w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/07/image-5.png" class="kg-image" alt="EventSystem raycast debugging guide" loading="lazy" width="413" height="328"></figure><p>My <code>Important</code> happened to be behind Unity&apos;s <code>Default</code> which I think defines a 0 in integer value. So that&apos;s why the cookie didn&apos;t get clicked!</p><p>Moving the cookies over <code>Default</code> is no good either since that will affect rendering.</p><p>Solution : Add <code>SpriteRenderer</code> to my box 2D collider though I didn&apos;t want any graphics, then set layer to maybe <code>NotImportant</code> so it loses in sorting. At least I know how to debug this now.</p><p>My problem is solved but there are more to go.</p><h3 id="depth">Depth</h3><p>This is different from camera depth earlier. It seems to be for equal distance graphics to win over each other. Which means the UGUI system. They are often on the same plane. Look at this comment :</p><pre><code class="language-csharp">        /// &lt;summary&gt;
        /// Absolute depth of the graphic, used by rendering and events -- lowest to highest.
        /// &lt;/summary&gt;
        /// &lt;example&gt;
        /// The depth is relative to the first root canvas.
        ///
        /// Canvas
        ///  Graphic - 1
        ///  Graphic - 2
        ///  Nested Canvas
        ///     Graphic - 3
        ///     Graphic - 4
        ///  Graphic - 5
        ///
        /// This value is used to determine draw and event ordering.
        /// &lt;/example&gt;</code></pre><p>This is why dragging around UGUI object in the Hierarchy tree has effect in both draw order sorting and also winning over in raycasting. This won&apos;t applied to non-UGUI objects.</p><h3 id="distance">Distance</h3><p>Finally, is it surprising that distance comes the last? This is why I moving the cookies a bit closer to the camera doesn&apos;t work out because there are many other criteria that came before.</p>]]></content:encoded></item><item><title><![CDATA[Looking into Unity's async/await]]></title><description><![CDATA[async already works in Unity without any kind of plugins or coroutine wrapping the Task and pseudo-async it by checking completion every frame. But it is kind of magical. Let's try to dig into it a bit more.]]></description><link>https://gametorrahod.com/unity-and-async-await/</link><guid isPermaLink="false">623c5d7df445b7063911bf49</guid><category><![CDATA[Unity]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Tue, 03 Mar 2020 09:19:50 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1501678853259-605c25a8eb8b?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1501678853259-605c25a8eb8b?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Looking into Unity&apos;s async/await"><p><code>async</code> already works in Unity <strong>without</strong> any kind of plugins or coroutine wrapping the <code>Task</code> and pseudo-async it by checking completion every frame. But it is kind of magical. Let&apos;s try to dig into it a bit more.</p><p>(Note : I am still confused by the whole C# <code>async</code> / <code>await</code> inner workings, I should add more once I understand everything throughly.)</p><h2 id="example">Example</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-2.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><p>I would like a <code>Button</code> that when clicked, would play a classic <code>Animation</code> on the box. But there is a catch : the button should turn into disabled state (greyed out via <code>.interactable</code>) <strong>until </strong>the box finished spinning.</p><p>But I would like a cleaner code, I want to use <code>await</code> until the box finished spinning and immediately put a line that <code>interactable</code> is restored right after. Instead of something like spawning coroutine host game object to check the state every frame and do work afterwards. Having code in the same place linearly is a big plus to readability. It&apos;s very fun to write such code.</p><p>With a code this simple, it already could achieve what I said. ( <code>async</code> method still show up in Unity&apos;s delegate picker just fine, don&apos;t worry.)</p><pre><code class="language-csharp">using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class SpinBox : MonoBehaviour
{
    public Button button;
    public Animation ani;

    public async void SpinAndDisableButton()
    {
        ani.Play();
        button.interactable = false;
        while (ani.isPlaying == true)
        {
            await Task.Yield();
        }
        button.interactable = true;
    }
}</code></pre><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/ezgif-7-37370b154b73.gif" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><p>Questions that should come to your mind : </p><ul><li>&quot;Who&quot; is &quot;running&quot; the <code>while</code> and the rest of method? Why is it getting checked in the next frame magically when the call to <code>SpinAndDisableButton</code> is only once, and there is no <code>Update</code> or coroutine whatsoever to repeatedly run it.</li><li>What is the timing of each run?</li><li>What is <code>Task.Yield()</code> ? It seems to be the key to everything here. I assume you used to <code>yield return null</code> in coroutines. That is a clever to say try again the next frame but you are talking via C# enumerator. The &quot;yield&quot; wording is similar, and even the behaviour is similar.</li></ul><h2 id="synchronization-context">Synchronization context</h2><p>Check out this article : <a href="http://hamidmosalla.com/2018/06/24/what-is-synchronizationcontext/">http://hamidmosalla.com/2018/06/24/what-is-synchronizationcontext/</a> how the rest of the program could be captured and continued in a way we like (e.g. on which thread? on the caller or on the thread that run the task?)</p><p>Then we will see Unity also have such a thing too but it is not visible to us at first glance.</p><h2 id="compared-with-regular-c-program">Compared with regular C# program</h2><p>It&apos;s not quite like normal C# program. Looking at <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/await">this official <code>await</code> documentation</a>. <code>await</code> means return to the caller as a <code>Task</code>. The caller could go on, until it need the execution result and can&apos;t afford to do something else in the mean time anymore (it doesn&apos;t matter the task is truly multithreaded or not) then caller can <code>await</code>. This chain can continue until you finally arrive at <code>Main</code>. Where if also <code>async</code>, there is an <a href="https://www.tabsoverspaces.com/233656-how-does-asynchronous-main-work-under-the-hood">another await</a> generated by the compiler that ask for result immediately.</p><p>Now look at our <code>SpinAndDisableButton</code> again. <code>await</code> returns to who? We have no <code>Main</code> in Unity as it is tucked deep in the engine code. The question is now the same as who is running <code>Update</code>, <code>LateUpdate</code> and so forth. It is the <code>PlayerLoop</code> API that the engine code run as a part of game loop to ensure orderly render submission and things goes in frame unit. But we used to not care since until now those entry point returns <code>void</code>.</p><p>Now the <code>await</code> is returning the execution point to that someone in hope that it could continue from the same point later, at a certain moment, automatically. Then <strong>continue with the game loop </strong>while <code>await</code> that. Otherwise we wouldn&apos;t seeing a spinning box at all if we really <code>await</code> until the box finishing the animation, because animation can&apos;t finish unless the frame goes on. What exactly would happen the next frame?</p><p>This is a pseudo code we want to achieve in a frame other than the frame that we pressed the button and disabled the button : </p><pre><code>for (gameLoop)
{
    if(box animation finished)
    {
    	Button is enabled.
    }
    Animation advances.
    Send box&apos;s transformation matrix for rendering.
    We see a new box&apos;s rendering.
}</code></pre><h2 id="debugging">Debugging</h2><p>Putting more logs into the code and we will try debug stepping.</p><pre><code class="language-csharp">using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class SpinBox : MonoBehaviour
{
    public Button button;
    public Animation ani;

    public async void SpinAndDisableButton()
    {
        Debug.Log($&quot;Started async {Time.frameCount}&quot;);
        ani.Play();
        button.interactable = false;
        while (ani.isPlaying == true)
        {
            await Task.Yield();
        }
        button.interactable = true;
        Debug.Log($&quot;Finished async {Time.frameCount}&quot;);
    }

    public void Update()
    {
        Debug.Log($&quot;Update {Time.frameCount}&quot;);
    }
    
    public void LateUpdate()
    {
        Debug.Log($&quot;Late Update {Time.frameCount}&quot;);
    }
}</code></pre><p>The update order turns out like this :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-7.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-8.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><ul><li>The frame where we start the animation, it came before <code>Update</code> and <code>LateUpdate</code> because the mouse clicking is thanks to <code>EventSystem</code> and <code>GraphicRaycaster</code> of Unity UGUI, which happens to have <code>Update</code> execution order before any of your scripts. </li><li>The <code>while</code> awaiting seems to occur <strong>magically </strong>every frame from that point, the timing is <strong>after </strong><code>Update</code> but before <code>LateUpdate</code>. It like we have spawned a coroutine but we haven&apos;t!</li><li>One interesting point is that at the first frame where we just issue the animation playing, there is 2 <code>Awaiting</code> in the same frame since UGUI event system came even before. Suggesting that <code>await</code> &quot;subscription&quot; is immediately effective without need to summarize or anything in the next frame.</li><li>In the old days we would have to put a check in <code>Update</code> or something. This await subscription elimiates the need of that.</li></ul><p>What&apos;s left is to demystify the &quot;awaiter&quot;, which I didn&apos;t get it completely but at least I see it works because it was intentionally coded in.</p><p>The first frame where I clicked the button there is nothing strange. <code>Update</code> (the same magical <code>MonoBehaviour</code> <code>Update</code>) of <code>EventSystem</code> uses <code>Input</code> API and see that I clicked, it pseudo-raycast into my canvas button and see that it could do something. Then it invoke into this <code>public async void</code> and arrive at this line. (You can manually use this <code>ExecuteEvents</code> too! It is a helper <code>static</code> class.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-9.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><p>The next frame around where magic happen, the call stack could reveal exactly who is working the check for us every other frames. This is the point where I didn&apos;t exactly understand what&apos;s happening here. (though it works)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-10.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><p>When checked the <code>UnitySynchronizationContext</code>, many seems to be called from engine code so I can&apos;t really do anything but guess.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-13.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><p>But there seems to be something called <code>WorkRequest</code> that represent each of your unfinished business <code>await</code>ed. I could guess that the <code>await</code> returning is properly registered, in a &quot;game&quot; way, that is friendly with frame paradigm and ensure all safe code locked in main thread, so you can truly do anything after any <code>await</code> because it is at the right moment in the frame.</p><h2 id="task-yield-">Task.Yield()</h2><p>This method got really confusing summary : </p><p><a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.yield?view=netframework-4.8">https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.yield?view=netframework-4.8</a></p><blockquote>Returns<br><a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.yieldawaitable?view=netframework-4.8">YieldAwaitable</a><br><br>A context that, when awaited, will asynchronously transition back into the current context at the time of the await. If the current <a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext?view=netframework-4.8">SynchronizationContext</a> is non-null, it is treated as the current context. Otherwise, the task scheduler that is associated with the currently executing task is treated as the current context.<br><br>Remarks<br><br>You can use <code>await Task.Yield();</code> in an asynchronous method to force the method to complete asynchronously. If there is a current synchronization context (<a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext?view=netframework-4.8">SynchronizationContext</a> object), this will post the remainder of the method&apos;s execution back to that context. However, the context will decide how to prioritize this work relative to other work that may be pending. The synchronization context that is present on a UI thread in most UI environments will often prioritize work posted to the context higher than input and rendering work. For this reason, do not rely on <code>await Task.Yield();</code> to keep a UI responsive. For more information, see the entry <a href="https://devblogs.microsoft.com/pfxteam/useful-abstractions-enabled-with-continuewith/">Useful Abstractions Enabled with ContinueWith</a> in the Parallel Programming with .NET blog.</blockquote><p>I am not English native, and I think neither C# <code>yield return</code> or <code>Task.Yield()</code> convey the function it performs. But let&apos;s go with the method definition.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-14.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure><p>An intended use case in the remark seems to say that the method is actually not <code>async</code> but you would like to turn it into <code>async</code> so you need an <code>await</code> somewhere in it, therefore <code>Task.Yield()</code> is the perfect scapegoat, or something. Instead of finishing the method right away (remember that <code>async</code> won&apos;t magically turn the method to async, it depends on the content of the method) now you <strong>force </strong>it to be async.</p><p>But in our case the context receiver is not an ordinary context but <code>UnitySynchronizationContext</code>. Now the <code>Task.Yield()</code> has a more useful function that effectively continue things the next frame. If it was an ordinary <code>SynchronizationContext</code>, I guess it could produce infinite loop in our example program since it would continue to the <code>while</code> again right away.</p><p>It returns a <code>YieldAwaiter</code> that the caller could use to continue. As evidence from our log, &quot;Awaited&quot; was logged every frame onwards becuase the context (code next to the <code>await</code>) was saved into this awaiter. <code>UnitySynchronizationContext</code> do something magical and wait a frame and use this awaiter to continue, then it hit <code>while</code> and once again return a new <code>YieldAwaiter</code>. This probably continue adding a new <code>WorkRequest</code> for the code earlier every frame as a pending task until the <code>while</code> is <code>false</code>.</p><h2 id="unitask">UniTask</h2><p>There is a popular <code>UniTask</code> (<a href="https://github.com/Cysharp/UniTask">https://github.com/Cysharp/UniTask</a>) package that eliminates <code>SynchronizationContext</code> altogether and make a new kind of lighter task <code>UniTask</code> that binds to <code>PlayerLoop</code> API directly. You could then choose the timing when should the awaiting check would happen in the next frame. (Initialization? Late update?) But essentially, you know <code>await</code> works without any kind of plugin. It will be useful with Addressables where <code>AsyncOperationHandle</code> could be <code>.Task</code> that you can use with <code>await</code>.</p><h2 id="read">Read</h2><p>Here are more links that will reinforce your knowledge of C# <code>await</code> pattern :</p><ul><li><a href="https://devblogs.microsoft.com/pfxteam/await-anything/">await anything; | .NET Parallel Programming (microsoft.com)</a></li><li><a href="https://devblogs.microsoft.com/premier-developer/extending-the-async-methods-in-c/">Extending the async methods in C# | Developer Support (microsoft.com)</a></li><li><a href="https://www.jacksondunstan.com/articles/4918">JacksonDunstan.com | How Async and Await Work</a></li><li><a href="http://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017/">How to use Async-Await instead of coroutines in Unity3d 2017 | Steve Vermeulen</a></li></ul><p>The last one highlights a particularly important weakness of Unity&apos;s iterator block pattern where you cannot return value or handle exception good enough, and the gotcha you may get when transitioning from non <code>async</code> method and just plainly call <code>async</code> method. If you are not careful, your exception will be <strong>swallowed </strong>into the <code>Task</code> and that is scary. There are some consideration of choosing between <code>async void</code> versus <code>async Task</code>.</p><ul><li><a href="https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation">Dixin&apos;s Blog - Understanding C# async / await (1) Compilation (asp.net)</a></li></ul><p>This one is so good! It demystifies that <code>async</code> and <code>await</code> disappeared into a regular method returning <code>Task</code>, while replacing the body with a state machine. The state machine&apos;s <code>MoveNext</code> will advance an integer each time. Each <code>await</code> in your body are sliced into sections belonging to each integer. And finally returning the result at the final state. You will see more clearly too how <code>GetAwaiter()</code> works and how your custom one will fare in the state machine.</p><h2 id="watch">Watch</h2><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/7eKi6NKri6I?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p>This is a good talk that show <code>await</code> already works, but there are pros of coroutines that justify its usage. You could stare at this slide for a while to get a grip if you are still confused.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/03/image-3.png" class="kg-image" alt="Looking into Unity&apos;s async/await" loading="lazy"></figure>]]></content:encoded></item><item><title><![CDATA[Visual Studio Code vs Rider for Unity]]></title><description><![CDATA[Visual Studio Code is almost perfect, very very few annoyances. But in this competitive scene of text editor, it is tempting to pay just to eliminate those annoyances from the tool I touch every day.]]></description><link>https://gametorrahod.com/visual-studio-code-vs-rider-unity/</link><guid isPermaLink="false">623c5d7df445b7063911bf3e</guid><category><![CDATA[Unity]]></category><category><![CDATA[Technology]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Sun, 15 Dec 2019 09:23:56 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1478877144596-fb7ee516e462?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1478877144596-fb7ee516e462?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Visual Studio Code vs Rider for Unity"><p>I switched from Atom to VSCode years ago simply because I like blocky and flat UI, but soon I discovered that VSCode has just the right amount of features yet well tucked away from view. But what leads me to evaluate Rider now?</p><p>Visual Studio Code is almost perfect, very very few annoyances. But in this competitive scene of text editor, it is tempting to pay just to eliminate those annoyances from the tool I touch every day. (Of course, after knowing several years passed with no resolution about them.)</p><p>Rider is not even famous for these annoyances fix I would like to pay for. It is famous for its &quot;intelligence&quot; as an IDE or deep Unity integration, that I am not really care at the moment. I just want a beefed up text editor like VSCode but without little flaws. Let&apos;s see if Rider can fix them. And how to modify Rider so it looks more like VSCode, so I could get both bug fixes and simple looks.</p><ul><li>What better or worse things in Rider compared to VSCode.</li><li>How to make Rider more like VSCode for those who liked VSCode, but just want that one bug fixed or that feature added now without waiting for submitted issue to be resolved in VSCode.</li><li>I will go as far as very little polishes, since fundamentally all modern code editor could do the same job. It&apos;s these details that make a difference.</li><li>Rider version evaluated is 2019.3.</li></ul><h2 id="fallback-perpetual-license">Fallback perpetual license</h2><p>The first thing you may think as soon as you notice that the business model is a subscription based in their website : &quot;I don&apos;t want to subscribe to a mere text editor!!&quot; Well text editor is our tool of the trade and is hard to perfect so you may need to reconsider its worth.</p><p>Anyways, I would be fine with one-off payment. I was put off by the subscription model because of insecurity of having to pay indefinitely.</p><p>Until I see that they offer this. Basically, when you stop your 1 year subscription you simply stop receiving updates and <strong>can continue</strong> using your version back when you first started. Also you can extend for a few months if you would like to stretch the coverage to get new features that match your use case then you could stop there.</p><p>Unlike Adobe for example which they will prevent you from using the tools completely. This is very nice and made me change my mind about <strong>purchasing </strong>Rider. Yes, it feels more like purchasing this way and I may even continue the subscription because they are generous. <a href="https://sales.jetbrains.com/hc/en-gb/articles/207240845-What-is-a-perpetual-fallback-license-">More information here.</a></p><h2 id="vscodevim-vs-ideavim">VSCodeVim vs IdeaVim</h2><p>I rely on Vim key bindings daily, and <a href="https://github.com/VSCodeVim/Vim">VSCodeVim</a> has lingering problems that was unfixed for multiple years :</p><ul><li><code>gd</code> to jump to definition usually works fine, but the reverse <code>Control + O</code> often return back to wrong place or throw dialog box error.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-4.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><ul><li>The plugin doesn&apos;t count code refactor as an undo step, when you do something next to a refactor and undo, you undo that step <strong>plus </strong>the whole refactor.</li><li><a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets">Code snippets</a> with variable transformation doesn&apos;t work when this plugin is on.</li><li>Macro recording <code>qq</code> playback &quot;too literally&quot; and sometimes missing some movement commands causing my plan to fail. Also it is quite slow, if the command is long then I can see it doing things as I wait for it to complete.</li></ul><p>I evaluate IdeaVim for a bit, it seems to not have any of mentioned problem. But instead : </p><ul><li> <code>==</code> or <code>=i(</code> to format code in IdeaVim doesn&apos;t work, while it works in VSCodeVim.</li><li><code>gc</code> for commenting in visual mode is not implemented, but it is in VSCodeVim. I have to resort to <code>Cmd+/</code> normal shortcut.</li></ul><p>There is always something missing in any Vim emulation!</p><p>I understand that I should not feel entitled to have a bug-free VSCodeVim where it is an open source project with tons of open issues on GitHub. If I am not willing to contribute, then I have no right to demand things from them. However it is also true that I don&apos;t have time to study how to contribute, and IdeaVim is a company maintained solution that I could spend money on instead of time. I guess this is where money could motivate you and result in a more polished product.</p><h3 id="ideavim-is-fast-">IdeaVim is fast!</h3><p>Maybe this alone worth the price. As in, in VSCode when you turn off VSCodeVim even simple line movement command is slower than you press down arrow with plugin off. It is apparent in a command that jumps over distance like <code>Shift + ]</code>.</p><p>It feels like VSCodeVim went through &quot;something&quot; before it could move. Maybe you won&apos;t feel it, but I am a music gamer and a game developer. I can tell responsiveness difference. Using real Vim like the one in Terminal or MacVim, the feel of responsiveness is very similar to Rider, but not to VSCodeVim.</p><p>(I am not saying Rider is faster than VSCode, that is impossible since Rider is more of an IDE. But if only the IdeaVim part, then it is faster. And it worth noting here that this text editing is the core part of programming experience, not start up time.)</p><h2 id="omnisharp-vs-resharper">Omnisharp vs ReSharper</h2><p>Omnisharp works fine most of the time, including project-wide variable rename that&apos;s important to my workflow so I can name my variable mindlessly when drafting up quick code.</p><p>In a very complicated case however it fails. For example, this variable in a lambda-in-a-method doesn&apos;t auto complete, where it does in a simpler lambda. This pattern is important in Unity&apos;s ECS library.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/Screenshot-2019-12-10-20.46.12.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/Screenshot-2019-12-10-20.47.06.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>ReSharper is well, sharper...</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-15.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Also I should mention ReSharper <strong>start up faster </strong>than Omnisharp. VSCode has reputation of being very fast, but that&apos;s not counting the time until your C# project truly come alive. In Rider, starting up the program itself is quite slow. But it is fully usable soon after. In the end I think total time is about the same.</p><h2 id="code-completion-dialog-size">Code completion dialog size</h2><p>From the earlier screenshot, one thing VSCode get a win from me is the super compact code completion dialog. Rider one is way too wide and line height is too big. And there is no way to change this. You <strong>can </strong>drag the right edge to narrow it, but when it appears again it expands to the longest entry that maybe deep below and you are not likely to access it. In Unity ECS, the verbosity cause this dialog to get unwieldly almost always.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/Screenshot-2019-12-11-22.32.34.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>And I really hate it when it go upwards, erasing all my context that I am thinking with. This is because the height is too high. If I could set it to about 4-5 entries then it could have fit below just fine.</p><p>Here&apos;s from VSCode. Small and cute. There is no need to show the whole thing. <code>...</code> is fine 95% of the time. The documentation balloon is equally compact.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/Screenshot-2019-12-11-22.32.04.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Meahwhile look at this Rider documentation balloon. It&apos;s huge! And it can also appear if you linger around the code completion box for 1 second, adding up to the clutter. I ended up turn it off because it was too obtrusive and always decided to go upwards.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/Screenshot-2019-12-11-20.07.38.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>And it is hell with <code>ForEach</code> lambda that contains million of permutations of valid lambdas, basically you take a lag punishment for waiting more than 1 second inside a lambda, that I have to turn it off. Then when I am using a normal method I have to go to definition to see the signature... damn.</p><h2 id="code-completion-performance">Code completion performance</h2><p>Rider is slow on completion! The VSCode one feels like &quot;just&quot; a string replace. However in Rider you can type ahead of the result and the keystoke can catch up later. It must be because of reformatting stuff and other intelligence. I think I can live with this, but VSCode is more snappy when choosing a code completion. (Also with template/snippet)</p><p>This offset with faster Vim performance in Rider however. Moving around the cursor is faster in Rider.</p><h2 id="the-look-can-be-matched-somewhat">The look can be matched, somewhat</h2><p>I like VSCode&apos;s minimal and blocky look. Some don&apos;t like full blown IDE like Rider not because it has heavy startup time, but it <strong>looks </strong>heavy. You know that feeling when you open Android Studio. You feel tired even before you start working.</p><p>Startup time asides (I don&apos;t really care), I could strip off bloated look from Rider until it looks empty just like VSCode. The most important point is <strong>one level</strong> of top bar. Then one level of bottom bar. And then eliminate the surrounding side bar of features (with rotated text like &quot;Project&quot;, &quot;Ve.. that&apos;s so distracting) that make you feels like you are coding in a box. You go to <code>View &gt; Appearance</code> to do this. Anti aliasing off on fonts make me alert, it is possible in Rider too in the preferences.</p><p>Target</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-13.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Before</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-20.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>After</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-14.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>With side bar off. Just top and bottom border now.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-18.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h3 id="editor-theme">Editor theme</h3><p><a href="https://rainglow.io/">Rainglow theme</a> is also available on Rider. I like changing themes depending on mood rather than trying to &quot;master&quot; a theme, so that&apos;s perfect. But there is one thing I like in VSCode, the theme extends to the side bar and not just the editor area. For example this more brown theme, the Rider dark that was fine in the screenshot earlier now looks weird because there are now 2 kind of different temperature of dark. But I hide the sidebar when not used anyways.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-17.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="taming-rules">Taming rules</h2><p>Rider came with rules built-in and my code are all moaning. Luckily I can tame them by clicking the light bulb and select what I want instead of digging preferences.</p><p>I do think they are too much sometimes, such as the <code>var</code> naming rules. They offer a rule about do/don&apos;t use <code>var</code> for simple type, or do/don&apos;t use <code>var</code> for when it is &quot;evident&quot;. But in real code it is more complicated than that. When I use <code>ToComponentDataArray&lt;T&gt;</code>, it is to my eye &quot;evident&quot; that the type is gonna be <code>NativeArray&lt;T&gt;</code> but of course the rule can&apos;t &quot;see&quot;. The <code>out var</code> pattern is often conflicting with some rules. And finally sometimes I arbitrarily use or not using <code>var</code> based on how busy the surrounding. (The combined <code>JobHandle</code> need it for emphasis, but the handle returned from <code>out var</code> I don&apos;t want to type <code>out JobHandle</code>, etc.) I ended up turning off many of the rules.</p><p>In VSCode&apos;s first few days I do have to turn something off such as code lens (I hate when my characters move around on their own) or folding, but there are more to turn off in Rider. I spent about 2-3 hours to config Rider fully.</p><p>Some rules are annoying though, like <code>float</code> <code>==</code> where I used 0 as special value to trigger some logic. (I don&apos;t want to make a new <code>bool</code>)</p><h2 id="code-snippet-vs-live-templates">Code Snippet vs Live Templates</h2><p>Rider&apos;s Live Template wins.. because it works with IdeaVim while VSCode Code Snippet cannot perform parameter transform while in VSCodeVim. Yeah it&apos;s all about Vim for me, can&apos;t be helped...</p><p>Here is an example of snippet config file from VSCode. Each <code>$</code> represent where your cursor will go on each tab. You can perform some transformation with regex on them. See more : <a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets">https://code.visualstudio.com/docs/editor/userdefinedsnippets</a></p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-22.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>In Rider, I like that I can name my variable whatever I want like <code>$type$</code> here. I haven&apos;t read yet how to perform variable transform. (So the type name inside <code>&lt;&gt;</code> could also become variable name but in camel case, for example.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-21.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="follow-deep-into-the-package">Follow deep into the package</h2><p>VSCode do not understand Unity, and when you try to follow something into the package, it ask the C# project file and found that it is a <code>dll</code>, and so it dig into the <code>dll</code> (generated from <code>asmdef</code>) instead of an actual package source code that is also available somewhere else.</p><p>Rider is winning here, because it knows about Unity UPM packages. If you follow into &#xA0;<code>EntityCommandBuffer.RemoveComponent</code> for example :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-23.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-24.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>You can get into the package with Rider. This is very important in modern Unity as source code are all available. You no longer have to wonder what happen inside when you can just <code>gd</code> into it then out quickly with &#xA0;<code>Ctrl+O</code>. (Vim commands) It is possible to turn off all the documentation popup with this. I think it is better as popups are distracting, let the code completion box be the only one.</p><p>(Though the VSCode <code>dll</code> version one is useful to quickly see the neighboring overloads such as which one has <code>EntityQuery</code> overload, though you can use the outliner in Rider too.)</p><h2 id="peeking">Peeking</h2><p>Red hot in Rider 2019.3, you can do this to peek the definition : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-25.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>VSCode could do it from long ago. Personally I like the VSCode one better because it works well with its &quot;boundless design&quot; by filling horizontally. Also..</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-27.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="matching-find-usages-behaviour">Matching find usages behaviour</h2><p>That peeking UX is very well used in find usages. Now the part at right has a browsable list.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-28.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>I don&apos;t like this massive tree of usage result that Rider presents me. It&apos;s clunky, and I can&apos;t see the code immediately like in VSCode while retaining old context.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-26.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>VSCode later even improve by adding a more complete version of find usages, which is still better than Rider&apos;s presentation. Just a file and lines where that appears. No need to present me a tree. </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-29.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Luckily there is a way to match Rider to this look. First, click the cog and select Left Top.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-34.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>And to eliminate useless folder entries on the tree and make it simpler like VSCode, it&apos;s in this Group By menu. Uncheck things from top until it is useful for you.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-35.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>If you like the peek style in VSCode, move to Bottom Left again and select the bottom left corner button &quot;Preview Usages&quot;. </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-36.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>The VSCode one again for comparison. It is just that the code is on the opposite side.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-28.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="matching-file-browser-behaviour">Matching file browser behaviour</h2><p>Click this not so descriptive &quot;Always Select Opened File&quot; button with down arrow so it works like VSCode. When you use Search Anything, the browser then focus on that file. I prefer this more than breadcrumb at top (which I turned off) because I could see the surrounding contexes and I could eliminate the height of top bar to just the tabs like in VSCode.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-37.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>That button on the left with opposite side of arrow, make it so a single click open the file like in VSCode. (The icon looks equally no so descriptive..)</p><h2 id="rename-refactoring">Rename refactoring</h2><p>In VSCode it is deceptively simple, but feature complete. On pressing F2, there is a little box where if you type something else it updates the entire project such that there is no error. (If the name didn&apos;t conflict.) And it is fast!</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-30.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Rider is not only slower as it is more careful to update an entire web of project, and also comments, but I don&apos;t know why sometimes it gives a dialog box. (And this version is quite slow.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-31.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Sometimes the same shortcut gets me a little box instead (I want this all the time...) I guess it is when it is a local variable?</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-32.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Also I am <strong>not</strong> a fan of Rider&apos;s small box rename that constantly updates the same thing nearby <strong>as you type. </strong>It seems to be more impressive that way, but I like the way VSCode did that they only commit ripple changes after you press enter.</p><h2 id="unity-aware-project-browser">Unity aware project browser</h2><p>Something I found nice, Rider&apos;s looks like Unity&apos;s Project panel. The blue box indicates a folder with <code>asmdef</code> inside it. It works with nested <code>asmdef</code> too.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-38.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>At first I was not a fan of colored icons, but I do look the cartoonish bordered look on them. The shade of yellow they choose is nice, fortunately.</p><h2 id="annoying-save-behaviour">Annoying save behaviour</h2><p>Rider seems to love to save your file automatically, or do something too transparent that you ended up feeling insecure if it was saved or not. This is <strong>hell </strong>with Unity where iteration time is a very well known problem.</p><p>For instance when you tab back to Unity all files are automatically saved. You cannot left the file in dirty state and go take a look at Unity and come back. You will have to face the 5-10 seconds freeze every damn time. Luckily you can turn this behaviour off :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-39.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>More weird things you cannot turn off : </p><ul><li>When closing dirty file it will not ask you anything, it will just close it after saving automatically for you. Seems nice, but sometimes I perform stupid edit and wanted to throw those away but now I have to repeatedly press undo. This maybe better if you are more discipline with your version control so you can only revert back to a commit, but it made me feel less in control by removing the save dialog.</li><li>You cannot save a file individually. There is only &quot;save all&quot;. This is also weird but in VSCode I always press save all anyways. For me this is welcomed.</li></ul><h2 id="auto-recompile-after-saving">Auto-recompile after saving</h2><p>After fixing the previous annoying point, it turns into a new <strong>good</strong> <strong>feature</strong>. Now you can press Save All while in Rider, then you will see Unity reacts and recompile as if you performed the Alt+Tab x2 trick you used to do in VSCode to force Unity to recompile.</p><p>I am getting used to this. Usually it goes like : I am done with a section of code and press save manually. I spend time glaring at it for a few seconds, then think &quot;Ok double checked. Nothing wrong!&quot;. But by the time I finished checking, Unity has done compiling my new code since it triggers as soon as I press save. It now feels like tabbing back to Unity is instantaneous.</p><p>If Unity managed to fix their iteration time to be under 500ms in the future, this will be even more beneficial.</p><h2 id="you-can-limit-tab-count">You can limit tab count</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-63.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>As you follow definition and open many files you ended up with a lot of tabs. I like this option that automatically close tabs for me. Though, it is a bit annoying when it close an unsaved tab and while it did save, it triggers Unity recompilation making my machine lags a bit.</p><h2 id="perform-file-operations-as-you-like">Perform file operations as you like</h2><p>VSCode at least have mass-rename or separate class to a new file, where it talks properly with Omnisharp and maintain its sanity. However there are limits. You cannot just new C# file or rename it in project panel in VSCode and have that file works flawlessly until you Alt+Tab 2 times to Unity and back forcing it to recompile, include the file in the C# project, then finally Omnisharp kind of &quot;restart&quot; and know what to do.</p><p>As expect of a more IDE-like Rider, you can new file and move file around as much as you like without switching to Unity and ReSharper still works. This is quite huge as in Unity these operation causes a spinning compile wheel of doom, followed by one long freeze before and after. It maybe well over half a minute each time.</p><p>Also you could do it all while the file is unsaved (dirty). VSCode Omnisharp often fail randomly in on some refactor commands if you didn&apos;t save the file or sometimes, wait for the resulting compile to finish first . I noticed that Rider never fails. Splitting files, symbol renames, dragging the files to other folders like crazy, while unsaved. It always work!</p><p>Here&apos;s what usually happen when you delete <strong>just 1 file </strong>in VSCode&apos;s Explorer panel. That file is <strong>empty </strong>because I just refactored things out of it.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-8.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>How to fix it</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-9.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>And sometimes this doesn&apos;t work until you restart the VSCode, with a random combination of restarting Unity or press Open C# Project in Unity after cleaning up all <code>sln</code> and <code>csproj</code>. File operation in VSCode with Omnisharp is just too brittle.</p><p>Edit : Something like this wouldn&apos;t resolve no matter how you restart Unity or VSCode or Omnisharp, sometimes restarting the computer helps, but I would rather pay then dealing with Omnisharp&apos;s tantrum... VSCode is a good editor as long as it doesn&apos;t touch Omnisharp.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-10.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="recursive-error-indicator">Recursive error indicator</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-41.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>At first I hate it, but now I <strong>love</strong> it. When you perform some manual refactoring that automated tools couldn&apos;t handle, or even some arbitrary edit, often times you are partly <strong>aware</strong> that something should break after doing this. Therefore you want to fix those next. Often I don&apos;t even want to read the error because I know it gonna error, I want to see where. And the way to do that is to double click follow the error anyways though I don&apos;t want to read them.</p><p>Usually I have to &quot;Alt+Tab x2&quot; to nudge Unity to recompile and update and produce a new error. But in Rider you see red squiggle appearing recursively to the outer folder what went wrong because of it, without reading the error log. Reading takes time, but you just want to know where quickly and when you see the code you may know what to fix even before reading what the error has to say.</p><p>It also applies to place like Search Anything. This is nice as most of the time that is probably the file I want to go.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-52.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="find-usage-write-access">Find usage : Write access</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-42.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>One extremely valuable feature in Rider not in VSCode, is the Find Usages has category based on read or write. Usually the source of problem in your test where you read, is of course to find the places where it was written. I usually have to look for <code>=</code> sign in VSCode manually or include that in a project wide search. This is very convenient.</p><h2 id="unit-testing">Unit testing</h2><p>You can run Unity tests from Rider. You can only run pure NUnit test in VSCode.</p><p>It seems Rider wins but actually no one wins beccaues it is SO SLOW! I would rather double click the one in Unity. It is so slow I got enough time to casually screenshot the &quot;Pending&quot; state of some unit tests which should be lightning fast.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-44.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>There is a nice button to click on the left of test method so you can run that test, similar to VSCode but it works with Unity. However, it is SLOW so I would rather Alt+Tab to run it in Unity anyways.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-43.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Tracepoint (breakpoint that logs instead of stopping program) didn&apos;t link to Unity&apos;s Console. Also there is a questionable auto complete in here, the <code>eight</code> you see is a valid log because I overridden <code>ToString</code> of it. However the auto complete randomly suggest things not depending on the current scope.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-46.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>The output appears here in &quot;Debug Output&quot; panel. But it is often flooded with other messages. Ideally I want to see the log in Unity and not here.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-47.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="generate-equality-members">Generate equality members</h2><p>Implementing <code>IEquatable</code> maybe more required than ever for Unity DOTS. This is a very welcome feature in ReSharper that is not available in Omnisharp. For example in some place like <code>ISharedComponentData</code>, ECS library may need your help to judge what is equal and what is not.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-48.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>You are then present with a dialog box that you can manually choose which values matters in an equality test.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-49.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="generate-relational-members">Generate relational members</h2><p><code>IComparable</code> is no less important than <code>IEquatable</code>. &#xA0;It is already used in multiple places in Unity ECS. For example sorting. <code>NativeArray&lt;T&gt;</code> or <code>NativeArraySharedValues&lt;T&gt;</code> could sort if <code>T</code> implements <code>IComparable</code>. In ECS we cannot ensure order of entities coming out from database. Some algorithm sometimes need sorting.</p><p>And when you implement <code>IComparable</code>, you remember that &quot;compare this first then if it is equal, see that next and so on.&quot; tedious pattern. The command Generate relational members could do it for you in an order you want.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-25.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-26.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-27.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>If you add more members and want to take them in account, you can delete the <code>CompareTo</code> method then use the same command again.</p><h2 id="generate-dispose-pattern">Generate dispose pattern</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-53.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>This is equally useful in Unity DOTS where you manage your own memory. It scans all your <code>IDisposable</code> and DOTS follows them.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-54.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="generate-switch-labels">Generate switch labels</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-55.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-56.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Nice. In DOTS, switch casing on <code>enum</code> is preferred for high performance jump table generated in assembly.</p><h2 id="overloaded-operator-coloring">Overloaded operator coloring</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-28.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>In Rider you can see an operator with custom behaviour as differently colored by the color theme. Plus you can also use go to definition on the operator. This above example highlights that it is <code>int4</code> SIMD operation from <code>Mathematics</code> library, not just C# built-in addition.</p><h2 id="local-internal-upm-workflow">Local/internal UPM workflow</h2><p>I have <a href="https://gametorrahod.com/how-to-asmdef-upm/">already highlighted</a> how important you should UPM your own project even for those you not intended to open source.</p><p>In VSCode, your project is a folder. When you follow a code into your own UPM package, Omnisharp is able to do so because it ask <code>.csproj</code> about them. And you can also edit the file if that UPM is on local.</p><p>In Rider you can do the same, except you can see the project tree of that package. Because Rider knows Unity! A folder with <strong>arrow and a box</strong> is local UPM.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-50.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>I think this is nice because in VSCode I was limited to use Go To Definition to go to package&apos;s file and modify, I was not allowed to freely browse any other nearby files in the package and must find a way to other files by Go To Definition. It will not be highlighted in the project panel either because you are now out of your project. Rider make it so all packages (non-read only) feels like a part of your project, just like in Unity&apos;s project panel.</p><h3 id="easy-to-upm-your-game">Easy to UPM your game</h3><p>By extension of this, you <strong>can </strong>drag any file to the UPM folder while editing code. You know how painful it is to move file around in Unity. Every little touch cause the spinner of doom. Editing <code>asmdef</code> even display a dialog preventing you from doing anything in the meantime. (Though even if you could, the editor lag so bad you cannot click and drag anymore file anyways.)</p><p>Auto refactor works. Renaming things in the UPM package from your main project works and updates your main project too. Overall this is the key feature to modular and testable game code. (You can have an another Rider instance that is opening that UPM as a main package too, though you can edit your package from your main game <strong>just as easily</strong>. This is not the casse with VSCode.)</p><h2 id="breakpoint-vomiting-values">Breakpoint vomiting values</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-51.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>I really like this polish in Rider. When a breakpoint is hit, you see each line vomit out all of its possible innards in code comment color without even hovering a mouse and check on the stack list. Therefore it could work well in a code that you &quot;chop&quot; to multiple lines.</p><p>This helps greatly to understand the surrounding even the one I didn&apos;t intend to check on, but ended up being where the problem was propagated. I had fun with this feature.</p><p>It really helps too that the returned value is at the first in list, and it turns orange if there is any change compared to previous break. I could glance and see everything without tediously register each variable for view or fumble with the list below. It&apos;s right there in the code.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-19.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="documentation-auto-complete">Documentation auto complete</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-57.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>While it is also possible in VSCode via plugins to get <code>///</code> to become summary, and also possible in VSCode to get autocomplete inside the <code>&quot;&quot;</code> part of <code>see cref</code>, and it is also possible in VSCode to rename all the way to documentation, it is not possible to auto complete the <code>see cref</code> itself while it is possible in Rider. Small margin of victory but still, nice.</p><p>*If Rider team is somehow reading this, I would lose my mind if I could highlight lines in summary &#xA0;and make it go to <code>&lt;remarks&gt;</code> section via the cleanup menu!</p><h2 id="cutting-comment-to-a-new-line">Cutting comment to a new line</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-58.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>I was surprised by this polish also. If you press enter while in the middle of any comment, the new line will be prepended <code>//</code> to be a comment also. Of course when I do this I don&apos;t intend to type an actual code in the next line, it is 100% better than not doing it. I didn&apos;t know I need it before. It doesn&apos;t affect pressing enter at the end of line, or using <code>o</code> key in Vim emulation.</p><p>And if the next line has some spaces after <code>//</code> it would be formatted the same way. Great!</p><h2 id="almost-ideal-import-missing-reference">Almost ideal Import Missing Reference</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-59.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>This function is very important in VSCode, as I code more properly with <code>namespace</code>, with <code>asmdef</code> and finally with UPM, it adds some hassle of importing things into scope. Luckily I can always do this to resolve them.</p><p>In VSCode if you didn&apos;t have a reference in your <code>asmdef</code> then Omnisharp cannot resolve it for you. For example, <code>DefaultWorldInitialization</code> is currently red. VSCode cannot know that this particular class is in <code>Entities.Hybrid</code> assembly because you didn&apos;t link the <code>asmdef</code> yet. You go link it because you know, then it could help.</p><p>In Rider it is a bit smarter. It seems to <strong>know</strong> Unity to the level that it knows the class from packages linked to your <strong>project manifest </strong>but not yet to this particular <code>asmdef</code>. But even though the completion works and <code>using</code> was added to the head, it is still unusable because it cannot go to your <code>asmdef</code> file and add missing cross- <code>asmdef</code> reference for you. <em>Almost</em> perfect!</p><h2 id="resharper-is-more-stable-than-omnisharp">ReSharper is more stable than Omnisharp</h2><p>You know in VSCode when you do something too &quot;outrageous&quot;, it feel like Omnisharp crashed and stopped being smart. When you check the Omnisharp log in the popup console, you will see some errors there but Omnisharp will not recover on its own.</p><p>This is solvable with &quot;Restart Omnisharp&quot; command which you will have to wait about an equal startup time of typical IDE like Rider. It is just VSCode that startup fast, not Omnisharp. (depending on project)</p><p>I sometimes see Rider goes all red too, but after a few moments it seems to be able to get a hold of itself fast. Granted this cause some performance hit but still better than having to restart the language engine often.</p><p>I suspect this &quot;goes crazy&quot; moment is when Unity decided to flip the table and recompile too many links (in not an incremental way, etc.) and Rider/ReSharper handles this more gracefully.</p><h2 id="one-backspace-to-bring-up-to-the-previous-line">One backspace to bring up to the previous line</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-61.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-62.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>When I would like to format the code so it go back to the end of previous line, I think there is no good Vim commands to do this fast enough other than cut and paste at the end of previous line. In Rider, pressing only 1 backspace at the beginning of the line for these 2 cases will bring it up, given that there are only empty spaces to be deleted.</p><h2 id="search-anything-includes-class-struct">Search Anything includes class/struct</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/12/image-70.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>It maybe a practice to separate class and structs to its own file. But in Unity Entities you probably have a LOT (100-200) of tag components used liberally, or a component with very little content (it is common for a component to have just 1-2 fields) which you ended up grouped together in a file, or even in the system file (which may make sense if you want it to be used by <code>.</code> notation after system name, like <code>ISystemStateComponentData</code>).</p><p>You then remember the struct name because you work with them always, but forgot what is the class name. Many times I have to find an instance somewhere just so I could use Go To Definition to go to it without knowing file&apos;s name.</p><p>In VSCode, class search only applies to the currently working file. In Rider you can type the class/struct name you want and it will go to the correct file. Unexpectedly convenient. I really miss this when going back to VSCode, it is difficult to remember which file I tucked that in. Sometimes the file was named exclusively, sometimes the file was named as one of the <code>struct</code> in there that I think is the &quot;boss&quot; of all other structs.</p><p>Also, if the class or struct is named the same but in different namespace, it could be easily discernable with the (in ...) followed the entry.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-34.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="search-anything-includes-methods">Search Anything includes methods</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/Screenshot-2020-01-05-23.18.54.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>It means <strong>any </strong>method in <strong>any </strong>file.. so it really is &quot;search anything&quot;. Why this matters? Because Unity&apos;s Test Runner has this annoying workflow where you have nowhere to click to go see your failing tests.</p><p>Because the test name is method&apos;s name, you can type it in search box and go to it regardless of its file. Note that there is no mention of file name in the Test Runner dialog. It is just namespace, class name, and finally method&apos;s name. Being able to go directly to the method is nice. But it would be nicer if the Test Runner failing message turns blue and let me click it..</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-1.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="autocomplete-virtual-abstract-methods">Autocomplete virtual/abstract methods</h2><p>This is one thing I thought VSCode Omnisharp could, but it couldn&apos;t! In C#, one of the pain point is to type <code>matching_accessor override ExactMethodName(SameType anyNameButYouRatherWantTheSame)</code> correctly. Editor function should help with this chore. Unity was crazy about magic methods like <code>Update()</code> for quite sometime, but now the direction is going to the righteous override path in <code>JobComponentSystem</code> of ECS, that make it more verbose to type.</p><p>Actually I remembered Omnisharp <strong>could</strong> with some difficult condition that it is not so useful. The criteria seems to be you having to type <strong>all the way </strong>to <code>protected override</code> then some characters, or something? Then the popup finally appears. Also see this issue :</p><p><a href="https://github.com/OmniSharp/omnisharp-vscode/issues/1044">https://github.com/OmniSharp/omnisharp-vscode/issues/1044</a></p><p>In ReSharper, it works as expected. I can immediately type the method name and they appears. On accepting the suggestion, <code>base.ThatMethod()</code> is automatically added, an another pain point that C# didn&apos;t force overrides to also call the base, but often you want it to that you ended up making a common routine on the base disappear. (Luckily in ECS there is nothing at the base.. yet.)</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-3.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-2.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>There are quite a lot of <code>override</code> to do in ECS to properly optimize the system. (Query ceremony, pre-allocated persistent native array for reuse, etc.) This is very useful.</p><h2 id="junk-entries-in-vscode">Junk entries in VSCode</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-5.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>In Go To function of VSCode, often I would see an entry that no longer works. Refreshing these search histories is possible, but in Rider unusable entries are never there. (But of course the performance is also less snappy.)</p><p>In Rider one thing I like is that when performing project wide search, when I check on each one and fix it, the &quot;baked&quot; search result will update accordingly to INVALID. This is a good middle ground between re-search and take performance hit or just left them be. In VSCode, the entry didn&apos;t change at all, and clicking on them will now go to random line that the search result was there.</p><h2 id="editor-attaching-breakpoint">Editor attaching / breakpoint</h2><p>In VSCode, I feel like sometimes I could get the breakpoint to activate but <strong>most of the time I cannot. </strong>I don&apos;t know an exact criteria, sometimes I have to restart Unity editor or VSCode and attach in a specific sequence.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-6.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Of course I have this on when I would like to use breakpoint.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-7.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>In ECS, it is quite important for unit test to use breakpoint where we couldn&apos;t use Entity Debugger. Usually you are questioned by how the system did run but didn&apos;t perform expected task.</p><p>In Rider <strong>editor attaching always work. </strong>I don&apos;t know what is the problem with VSCode but it just works in Rider. The attaching process is quite laggy, but as long as it works it pays off when you managed to find the bug.</p><p>Also with Burst off, the breakpoint even hit in the job code as well. I can hot swap Burst compilation while attaching and let the breakpoint work.</p><p>In VSCode, when trying to attach the editor usually even after I got it to attach and break properly :</p><ul><li>Doing something &quot;too outrageous&quot; would make the editor attaching lose its mood and stop breaking at all breakpoints.</li><li>Sometimes it freezes Unity and need to restart both VSCode and Unity altogether. I find it often happen if attempt to attach while in play mode. (In Rider, I could attach/detach at any moment.)</li></ul><h2 id="tracepoints-logpoints">Tracepoints / Logpoints</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-17.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>There is this practice of spraying log messages and just let the code run through them in order go get a grip about the situation. It may sounds rough but many times it is better than breakpoints because you can observe &quot;from the top&quot; order, patterns, and their frequency that would ultimately leads you to the solution why the test is not what you expected.</p><p>You may spray <code>Debug.Log</code> all over and remove them, which works when compile speed is fast. But it is not so practical to probe different place rapidly. Also in ECS I can&apos;t use <code>Debug.Log</code> then forget to comment it out, since Burst will pick it up and complain. Start using breakpoints/logpoints is crucial to ECS development than ever.</p><p>So let&apos;s use a proper function in the editor. VSCode has a <a href="https://code.visualstudio.com/blogs/2018/07/12/introducing-logpoints-and-auto-attach">Logpoint</a> feature for this, and equally in Rider, you can turn off Suspend and then turn on Log : Breakpoint Hit message.</p><p>Rider wins because : </p><ul><li>The attach works 100% that would allow me to use the feature in the first place.</li><li>You can log &quot;nothing&quot; when you don&apos;t know what to type, it will appear as a clickable link that jumps to that line of code. (As seen in the image, you can click any purple text besides &quot;Breakpoint reached&quot;) This is very useful.</li></ul><h2 id="run-execution-to-here">Run execution to here</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-20.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>I think this is an another killer feature. Often the break point is not quite right. Repeatedly press step forward takes time. Placing a breakpoint far ahead and resume execution works, but I have to remove it immediately after because that&apos;s kinda temporary breakpoint. &quot;Run execution to here&quot; solve this problem more directly. And the button even appears on the line you clicked.</p><h2 id="fine-tuning-color-theme">Fine tuning color theme</h2><p>No theme is made just for you. While browsing themes you may find color palette you finally like. But maybe that red was too dark, maybe you like that green more flashy or more pastel. Maybe everything was perfect except they make method argument too colorful and it spread to an entire method since you use the argument everywhere.</p><p>In Rider, other Preferences maybe a bit bloated, but they have a very good theme editor where you could exactly pinpoint what would you like to change by clicking the displayed bits of sample text in the box below. Also it supports inheriting in an easy to understand way. The only improvement I want is that they could add an Apply button where I see my real code behind the dialog updates accordingly.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-11.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>That was my adjustment to Rainglow&apos;s <a href="https://rainglow.io/preview/#zacks">Zacks theme</a>, which I like the color already except the red looks too much like error and purple requires too much attention to read so I made them lighter. Also the method argument was green, the same color as class name. Everything turned green in a method where I heavily use the passed in argument, and I don&apos;t like it. So I make the <code>( parameter )</code> you see above a cream color instead. It is close to white, but just enough so I could see what came from the top of method. Here&apos;s an original theme :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-12.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>In VSCode, to further customize a theme further one need to debug TextMate scope.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-13.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Then according to the returned score (like <code>storage.type.cs</code> here) you can override the color of the theme. </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-14.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>It is much more involving, but also it allows more fine grained adjustment. For example, it is impossible to color the &lt; &gt; in Rider. In Unity ECS, there are so many &lt; &gt; that I would like to make them a bit more stand out/more subdued. In VSCode, it has this <code>puctuation.definition.typeparameters.end.cs</code> that allow it to be themed.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-15.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><h2 id="rearrange-method-signature">Rearrange method signature</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-16.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>You can safely rearrange signature in a way that everything else updates accordingly.</p><p>You may think this is such an uncommon issue, but in Unity ECS, <code>static</code> method is going to be a very good way of sharing code between job functions which Burst could treat it as a part of code of each individul jobs. Previously we rely on OOP inheritance to reuse code.</p><p>This requires migrating a lot of things via argument so that <code>static</code> could work on what it was given. Able to rearrange them later let you just add required things without much thought at prototyping stage. (The user of the method would get updated arrangement as well, this is the biggest pain point on refactoring utility methods like this.)</p><h2 id="softwrap-and-line-up-down">Softwrap and line up-down</h2><p>When situation calls for vertical split while programming, it is important to use softwrap (virutal line break) to be able to read code without scrolling. Vim commands <code>Ctrl+w</code> (window) then <code>v</code> (vertical) does this. Then you can switch between them with <code>Ctrl+w Ctrl+w</code>.</p><p>In VSCode, the soft wrapped line is not counted as a new line. So <code>j</code> <code>k</code> commands in Vim emulation will skip to the next real line.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/licecap.gif" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>In Rider, they are counted as a new line.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/licecap-1.gif" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>It depends, &#xA0;but I like the way it works in Rider. Anyways, commands like <code>I</code> will still go back to the <strong>real </strong>beginning of the real line. (What I wanted)</p><h2 id="string-split-on-enter">String split on enter</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-18.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>The above result was made by placing the caret on the middle of string and press enter. The <code>+</code> and all other <code>&quot;</code> appears automatically. This is occasionally neat when you want some wild single line string to be more readable in code. (Unity&apos;s <code>[Tooltip]</code> comes to mind.)</p><h2 id="open-source-license">Open source license</h2><p>If you maintain an open source project (you could be already if you UPM your game and some of the package are generic enough) you can apply for ALL product open source license. It could be only used for developing open source projects. But it allows you to use ALL tools. Maybe you can also branch out to non-C# open source projects too.</p><p>It lasts 1 year, which you may apply again if the project is still eligible. As you see below, I get only Rider for normal projects, but I can use everything for my open source projects now. Related to Rider, are dotMemory, dotCover and dotTrace. But I have no experience with them yet.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/Screenshot-2020-01-23-13.26.12.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p><a href="https://www.jetbrains.com/community/opensource/">https://www.jetbrains.com/community/opensource/</a></p><h2 id="extract-method">Extract method</h2><p>Not sure how the light bulb in VSCode could do because I had never use it, but in Rider it is really good.</p><p>I would&apos;t care much in OOP, but in data oriented design, we no longer wrap and reuse methods by means of inheritance anymore. (e.g. human has leg and could <code>Walk</code> and so do a dog that has leg, so you write the <code>Walk</code> code once. etc.)</p><p>And now one of very good way to reuse code is to just write a <code>static</code> function. Take something in and give out results, functional programming style. Burst code that touch these shared static code will then think that the code &quot;belongs&quot; to itself. (So you get duplicated assembly code in the burst compiled file, but shouldn&apos;t matter as long as performance is good.) The other way to share code is to bake some instance method in the <code>struct</code> that could help modify the struct itself.</p><p>In this image below, it looks like the <code>TryGetValue</code> below is going according to the above ones. I noticed the pattern and therefore I would like to refactor out the above to also use on the one below.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-21.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Refactoring out works on what you highlighted precisely. The following popup then let you customize whether you want return value, or you want <code>ref</code> style. It seems to know what I would like to get from the highlighted code. I was amazed.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-22.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-23.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>This is the FP style that take values in and return the changed value.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-24.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>As you see when I prefer <code>void</code> as return type, then the generated method has to use <code>ref</code> for the value that connects with what&apos;s outside of my highlighed code.</p><h2 id="extract-local-function">Extract local function</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-29.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>C# has added ability to nest a local function (no even <code>private</code> accessor, it&apos;s private in scope and has its own capture rule kinda like lambda). It is a bit weird to write and read and you sometimes don&apos;t know where to place them, but still interesting and I would like to practice using it more to create a more &quot;functional&quot; function. A method that contains little pieces of logic assembled.</p><p>So, you can highlight and extrace a local function too, not just to the outer method. There is a dialog that ask you where, as local function could be anywhere.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-30.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>After that dialog, it knows which thing is external to the one you highlight and offer to turn that into a passed-in variable <strong>instead</strong> of a capture. This is very good to rapidly make a &quot;pure&quot; function out of your code draft. I am always in for things that could raise skill ceiling in programming and this encourages it.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/image-31.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>This is also useful for writing tests, as in test you often explicitly perform some sequences before the check. Then you want to do that sequence again to test repeatability, etc. A local function is the most logical way to do it since it does not make sense that other tests could also use this sequence specific for this test.</p><h2 id="final-verdict">Final verdict</h2><p>I used Rider until the last minute, and went back to VSCode for 2-3 days just to make sure how I feel about Rider.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/Screenshot-2020-01-10-11.11.03.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2020/01/Screenshot-2020-01-12-19.27.14.png" class="kg-image" alt="Visual Studio Code vs Rider for Unity" loading="lazy"></figure><p>Despite the general impression that Rider is slower, I ended up purchasing 1 year license of it. Here&apos;s top things that made me make this decision : </p><ul><li>Editor attaching and breakpoint hitting that always work. This is very crucial in ECS unit testing.</li><li>Finally some new skill ceiling to improve debugging skills because the attach always work. Especially, stop the <code>Debug.Log</code> spraying habit and use logpoints. I would gladly pay so I have something new to level up. (This is exactly how seasonal pass monetization in games works...)</li><li>The editor is resistant to all refactor actions be it from quick fixes popup or by you dragging things in Explorer.</li><li>It let me develop in packages with less friction.</li><li>IdeaVim is generally faster and better, especially how it could come back to the same place after <code>gd</code> into it.</li><li>Go to definition knows how to drill into UPM package source code and not <code>dll</code> version of them.</li><li>No more annoying Omnisharp red squiggles. (And Resharper start up faster than Omnisharp)</li></ul><p>I missed these in VSCode :</p><ul><li>More muted color design overall.</li><li>Better Preferences that looked much less bloated.</li><li>Lower battery drain when I am not plugged in.</li></ul>]]></content:encoded></item><item><title><![CDATA[How to remodel your project for asmdef and UPM]]></title><description><![CDATA[In this article I will guide you to adopt asmdef and (internal) Unity Package Manager in your existing project. It will be harder than if you do it from the beginning, but that's why I have written this guide.]]></description><link>https://gametorrahod.com/how-to-asmdef-upm/</link><guid isPermaLink="false">623c5d7df445b7063911bf21</guid><category><![CDATA[Unity]]></category><dc:creator><![CDATA[5argon / Sirawat Pitaksarit]]></dc:creator><pubDate>Fri, 13 Dec 2019 10:26:30 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1576180550593-acc2cdbf0f7c?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1576180550593-acc2cdbf0f7c?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="How to remodel your project for asmdef and UPM"><p>In this article I will guide you to adopt <code>asmdef</code> and (internal) Unity Package Manager in your <strong>existing </strong>project. It will be harder than if you do it from the beginning, but that&apos;s why I have written this guide. Also it is so that in the future, your new part of code could be in UPM properly for more orderly one-way reference.</p><p>This article assume you know <a href="https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html">basic benefits of it</a> and how it works with folder structure to group your scripts. This article will start by going over some benefits that may not be obvious to you, so that you are motivated to do it.</p><h2 id="benefits">Benefits</h2><ul><li>Compiles faster with the built-in incremental compiler. This is not even its full power, as I heard from the forum that Unity is prepping for no domain reload play mode enter and faster hot reload on script changes. That could be depending on how well you separate <code>asmdef</code> today. (A guess, but likely) Today, it do not affect enter play mode time unfortunately.</li><li>Allows you to name your folder that previously must be named exactly <code>Editor</code> as anything else, with <strong>editor asmdef. </strong>(But you should name them <code>Editor</code> anyways..)<strong> </strong>To do this, make sure Any Platform is unchecked, then only Editor is checked for Include Platforms.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-13.09.32.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><ul><li>Previously all your <code>Editor</code> folder scripts do not ended up in your game, now it is the same except that they could be separated by dll, decrease compilation speed for even editor code.</li><li>Test assemblies. They have their unique problem. To make an edit mode test assembly, do the same as editor asmdef except check that <strong>Test Assemblies</strong> box. This is the same as putting tests in the <code>Editor</code> folder.<br>However the real benefit is in making <strong>the play mode tests.</strong> Previously there is a problem that you want to test something in play mode the script must stay out of <code>Editor</code> folder, then they will be included in the game too if not removed manually.<br>Now with that <strong>Test Assemblies </strong>check box plus non-editor setup, you get something usable in play mode <strong>and even in real device test </strong>but somehow <strong>not in the real game build!</strong> To get a special behaviour of allowing play mode test in the real build, press this button. </li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-13.15.38.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Without <code>asmdef</code> this button is also usable but they will also be in your game for real.</p><ul><li>As a bonus you could rank your script&apos;s weight at glance in <code>Library/ScriptAssemblies</code>. I once caught an error here when one of my plugins included a huge baked constants that I should remove.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-13.22.21.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><ul><li>Make you an organized, clean coder, since it will not allow you to make messy dependencies. In a way, this is a unit test for code organization.</li><li><code>asmdef</code> pave way to UPM, which could share code between your projects or even open sourced online. This is the ideal form of module based development, but for starters let&apos;s try to segment an already messy game into <code>asmdef</code> without UPM first.</li><li>Will this make your game easier to hack when they could see all your dll separated up nicely? Maybe, but your single dll game is also hackable if the hacker wanted to see your symbols anyways.</li><li>Earn additional income by prototyping a would-be/potential Asset Store items as a package with thier own <code>asmdef</code> in their own project. Field test them in all your real projects easily without copy, with UPM link and <code>asmdef</code> link.</li></ul><p>UPM warrants its own topic, which is not the same as <code>asmdef</code> but closely related.</p><h2 id="unity-package-manager-is-going-full-asmdef">Unity Package Manager is going full asmdef</h2><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-12.49.28.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>For those who didn&apos;t catch up yet, we used to read Unity version release change log. Now they do it NPM style, each internal team that work on a feature could push an update on their own, each with its own <code>CHANGELOG.md</code>. This is a great initiative! All these fun stuff available to play with. (And more bugs to report!!)</p><p>UPM works with <code>package.json</code> at root of the package&apos;s folder much like NPM, but it is customary that you should do <code>asmdef</code> also. The official pattern looks like this :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-12.56.28.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><ul><li><code>Company.PackageName</code> : For the main part</li><li><code>Company.PackageName.Editor</code> : For the editor only part (references the main part)</li><li><code>Company.PackageName.Editor.Tests</code> : For unit test / edit mode test (references the main part)</li><li><code>Company.PackageName.Runtime.Tests</code> : For integration test / play mode test (references the main part)</li></ul><p>For example, my plugin <a href="http://exceed7.com/introloop/">Introloop</a> looks like this with an additional &quot;Demo&quot; assembly.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-13.48.35.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Since my demo scenes has some debug scripts to show how things work. When my customer wants to use the plugin, if they adopted <code>asmdef</code> already, they could include only non-demo part to their game. (If they don&apos;t, all non-editor <code>asmdef</code> will be referenced like usual) Also it helps sanity check me that I don&apos;t use something in the demo from my main part.</p><p>I will explain to UPM your own game along the remodeling section. But first let&apos;s see some obstacles that you must overcome.</p><h2 id="asmdefs-are-infectious">Asmdefs are infectious</h2><p>Your game completely devoid of <code>asmdef</code> was perfectly fine. But your game <strong>with just one </strong><code>asmdef</code> became quite troublesome. (Maybe you just want that test assembly, maybe you want to do it only a little at first)</p><p>They are &quot;infectious&quot; (similar to things like <code>async/await</code> programming) that if you don&apos;t do it all, you can&apos;t do it at all. To see why :</p><h3 id="overviews-to-keep-in-mind">Overviews to keep in mind</h3><ul><li>Anything in <code>asmdef</code> are now <strong>secluded </strong>from the world, where previously it knows everyone. You have to link up <code>asmdef</code> to get it to know each other.</li><li>Anything in <code>asmdef</code> still know all <strong>precompiled .dll </strong>like before. For example if you have Firebase Unity SDK which consists of closed source code packed in .dll, all your <code>asmdef</code> has access to it. You don&apos;t have to link them up. Except one dll ...</li><li>Everything not caught in <code>asmdef</code> will be in the catch-all <code>Assembly-CSharp.dll</code>. This .dll has one big problem : <strong>none of your <code>asmdef</code> implicitly know this dll, and cannot explicitly reference it. Not even with Override References.</strong> It must be the terminal node of the &quot;reference graph&quot;.</li><li>This includes if you decided to write a test in its test assembly while keeping everything else in the game as before (spilled out to catch-all dll) Now turns out the test cannot test anything, since the thing to test cannot be referenced to the test assembly. You face a dilemma because you don&apos;t want to ship test code either. Now you must <code>asmdef</code> an entire game, which brings to this article.</li><li>If you check <strong>Override References </strong>it will remove the mentioned &quot;know every .dll&quot; ability and let you explicitly define each one. The box below will not appear if you didn&apos;t check it. &quot;References&quot; in this context meant .dll only, not <code>asmdef</code>.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-12.28.11-1.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><ul><li>The link cannot form any circular dependency. This is the most common roadblock that make you give up on using <code>asmdef</code> and this article will advise how to avoid this.</li><li>This is why UPM package could be 100% <code>asmdef</code>&apos;d and should be, because by nature <strong>they are plugins</strong> and do not depend on something specific to your game. The reference direction is one way : your project use the package.</li></ul><p>Together, it cause trouble like these :</p><h3 id="can-t-reference-assembly-csharp-dll-problem">Can&apos;t reference <code>Assembly-CSharp.dll</code> problem</h3><p>You only want to pack up your play mode test code in an <code>asmdef</code> and not touch anything else in the project. But obviously your test code should at least check on something in your game, like if start at this scene and wait 10 seconds it should show this and that.</p><p>Sure, you can start a scene with <code>string</code>, and wait, and then use magic method like <code>GameObject.Find</code> to try to <strong>blindly auditing </strong>the environment without actually what they are. But these tests are brittle and break when you change the scene&apos;s content.</p><p>You want your test assembly to reference <strong>anything else </strong>but it is not possible to do it with <code>Assembly-CSharp.dll</code>. Check mate, you delete the <code>asmdef</code> and think this is a waste of time.</p><h3 id="the-bridge-problem">The bridge problem</h3><p> you see some folder that looks self-contained and would be nice to place <code>asmdef</code> on. (For example &quot;TitleScreen&quot;) By placing the <code>asmdef</code> on just this folder and let everything else go to <code>Assembly-CSharp.dll</code>, if any of the other scripts has a reference to things inside this TitleScreen folder, you are now out of luck unless you move it to the same <code>asmdef</code>.</p><p>What could be these &quot;bridge&quot; things? The most common is something that interconnect scenes. You may have <code>SceneTransitionManager</code> for example, which is like a <code>static</code> hub that was designed that everyone could call and move to the other scene, <strong>along with passing some data to the destination scene + clean up.</strong> The everyone-could-call part is fine, but if you have something like <code>TitleScreen.options.noIntro = true</code> in your scene transition, you are now shooting yourself with circular dependency.</p><p>Then you may think let&apos;s just move that into the TitleScreen&apos;s <code>asmdef</code>. Now the other screen&apos;s asmdef have to link to TitleScreen&apos;s <code>asmdef</code> for strange reason that they just want to access the utility. This is what I call &quot;the bridge problem&quot; since the bridge needs to know everyone, and everyone must call the bridge somehow, therefore the dependency is not one way and it prevents us from remodeling into <code>asmdef</code>.</p><p>How to fix this problem comes later while we are remodeling together.</p><h2 id="the-plan">The plan</h2><ul><li>The most logical unit of <code>asmdef</code> for your game I think is <strong>scenes.</strong> (depends on game genre still) We will try divide the <code>asmdef</code> equal to your scenes/screens. Like title, mode select, game play, credits, shop, etc. The name will be <code>Company.MyGame.SceneName</code>.</li><li>Plugins + your own multi-project common code should all be thrown away to UPM and 100% wrapped in <code>asmdef</code>. If it errors after you do this, the plugin is coupled to the game and you should not call it a &quot;plugin&quot;.</li><li>There is something called <code>Company.MyGame.Core</code>, which ALL scene <code>asmdef</code> could reference <strong>but not allowed to reference anyone else </strong>except plugins.</li><li>There is something called <code>Company.MyGame.Scripts</code> that try to catch anything else not fallen into &quot;Core&quot; or any of the scenes. This is not allowed to reference any scene assembly, <strong>but can reference the &quot;Core&quot;.</strong></li><li>When you achieve all these, you <strong>will not see </strong><code>Assembly-CSharp.dll</code>. Indicating that <code>Company.MyGame.Scripts</code> had taken place, but this time it removes the &quot;not able to reference <code>Assembly-CSharp.dll</code> problem!</li></ul><p>Example references : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-14.36.04-1.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>The arrow shows explicit reference that must be specified on <code>asmdef</code>. The .dll are implicitly known by everyone.</p><h2 id="step-1-upm-your-asset-store-plugins-unitypackage-you-bought">Step 1 : UPM your Asset Store plugins / .unitypackage you bought</h2><p>Most developers hadn&apos;t put either <code>package.json</code> or even <code>asmdef</code> yet. You could do this like so :</p><ul><li>Make a <strong>new</strong> Unity project called &quot;My Asset Store Plugins&quot; or something.</li><li>Use Asset Store in the editor to download the plugins.</li><li>Put <code>asmdef</code> in the plugin&apos;s folder accordingly.</li><li>Put <code>package.json</code>. Copy the pattern from Unity&apos;s official package. The version must be semver compatible (x.y.z)</li><li>Use Package Manager&apos;s <code>+</code> button to locate each local <code>package.json</code> as desired from your game project.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-14.57.53.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>This approach make it easy to update these plugins by coming to this <strong>&quot;plugin galore&quot; project</strong> and press update in Asset Store tab, so it overwrite files in their intended location, then the change will be reflected in ALL your games that use these UPM.</p><p>You should not get any errors even if no other part of your game became <code>asmdef</code> yet, since all your things should be in <code>Assembly-CSharp.dll</code>, and this dll knows all &#xA0;<code>asmdef</code> (including one that came from UPM) and all other dlls. This is why this is a step 1. Go make sure your game compiles now.</p><p>For <code>.unitypackage</code>, do the same thing except this time they didn&apos;t came from Asset Store, so you could put them wherever (even in a normal folder that is not Unity project! But you need a Unity project to unpack <code>.unitypackage</code> anyways). I put them together in the same &quot;My Asset Store Plugins&quot; project as my Asset Store items.</p><p>For example, I was able to UPM the <a href="http://esotericsoftware.com/spine-unity">Spine .unitypackage</a> by adding 2 <code>asmdef</code> and 1 <code>package.json</code> :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-14.50.39.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><!--kg-card-begin: markdown--><pre><code>{
    &quot;name&quot;: &quot;com.esotericsoftware.spine&quot;,
    &quot;displayName&quot;: &quot;Spine&quot;,
    &quot;version&quot;: &quot;3.7.0&quot;,
    &quot;unity&quot;: &quot;2019.1&quot;,
    &quot;description&quot;: &quot;2D animation for games.&quot;,
    &quot;keywords&quot;: [
        &quot;animation&quot;
    ]
}
</code></pre>
<!--kg-card-end: markdown--><p>Then it will show up :</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-15.00.02.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Some plugin cannot be a package since : </p><ul><li>They try to do something related to their script position, or write something to its folder (bad practice!) in that case you will see some errors as you use them. This doesn&apos;t mean you can&apos;t put <code>asmdef</code>. For example, I found that <a href="https://assetstore.unity.com/packages/tools/localization/i2-localization-14884">I2 Localization</a> as UPM package throws something about writing file to somewhere as I use them. (It compiles) But I am able to put just <code>asmdef</code> on and let it lives in the project. It is still better than nothing, just that if your another project want to use this you must copy, then update on each projects individually.<br></li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-15.02.05.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>One another instance is <a href="https://github.com/playgameservices/play-games-plugin-for-unity">Google Play Game Services (GPGS)</a>, where I could put <code>asmdef</code> and let it be in my project. But once I tried to do UPM, it complained : </p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-15.28.42.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Indicating that the plugin really wants that exactly named &quot;GooglePlayGames&quot; folder cluttered directly in yout <code>Assets</code>. Ugh..</p><ul><li>The plugin contains tons of dll, which are useless to <code>asmdef</code> in the first place. Some example of these are <a href="https://firebase.google.com/docs/unity/setup">Firebase Unity</a> and <a href="https://assetstore.unity.com/packages/tools/utilities/odin-inspector-and-serializer-89041">Odin Inspector</a>. You must let them sit normally in the project. .dll plugins aren&apos;t bad, in fact you are putting <code>asmdef</code> so that they became little .dll. .dll plugins aren&apos;t recompiled together with your project, but more difficult to debug.</li><li>They are arranged in strange folder combination. For example has multiple <code>Editor</code> folder sandwiched in multiple subfolder that you think it is a hassle to arrange them and have to do it again on the next update.</li><li>They wants to be in a special folder like <code>Plugins</code>. In this case, since you could have only one <code>Plugins</code> folder in your project you cannot use UPM.<br>You could try putting <code>asmdef</code> regardless inside the <code>Plugins</code> folder and if it works, it means it doesn&apos;t really need to be in <code>Plugins</code> folder! One example is <a href="https://assetstore.unity.com/packages/tools/utilities/maintainer-32199">Maintainer</a>, which is an editor-only plugin. I could left that in Plugins folder in my &quot;My Asset Store Plugins&quot; project, then from my game link to <code>package.json</code> inside that <code>Plugins</code> folder. You should not pull it out of <code>Plugins</code> even if you know they work regardless, since it will be easier to update from Asset Store if you keep the same shape.</li></ul><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-15.08.01.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><h3 id="take-meta-files-with-the-package-">Take .meta files with the package!!!</h3><p>The <code>.meta</code> file contains GUID which actually links things in the scene that use these scripts to the actual script. If some day I moved my Asset Store code somewhere without copying <code>.meta</code> file, I am breaking all my customer&apos;s existing projects.</p><p>Think of it as the identity/lifeline of each of your file. You can rename the code&apos;s file name as long as you also rename the <code>.meta</code> file (this is automatic if done in Unity editor), or else it will destroy the <code>.meta</code> file and generate a new one. <strong>This is a serious problem.</strong></p><p>The only somewhat negligable case is <code>.meta</code> file of folders. But those are still serializable by something like <code>UnityEngine.Object</code> on your <code>ScriptableObject</code>. It could still disconnect! For example my in-development plugin contains a feature that remembers folder to scan its content. If this folder&apos;s <code>.meta</code> file changes it won&apos;t be able to locate the same folder since all it did is actually just remembering the GUID, not the actual folder.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.21.47.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.25.38.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><h2 id="step-2-upm-your-own-shared-code-internal-or-not">Step 2 : UPM your own shared code, internal or not</h2><p>You may have only 1 game in development now, but as you develop please always try to think &quot;could this feature stand on its own?&quot;</p><p>All my plugins <a href="http://exceed7.com/introloop/">Introloop</a>, <a href="http://exceed7.com/native-audio/">Native Audio</a>, and <a href="http://exceed7.com/native-touch/">Native Touch</a> are all solution to some problems in my game. However I realized they could stand on their own and be reused on my future projects. Right now, that future project turned out to be other people&apos;s projects! They are able to be distributed on Asset Store thanks to portability ensured by UPM, plus field tested by them included in all my games with ease.</p><p>Even if you aren&apos;t going to sell them you still should. For example I have a collection of &quot;fun stuff&quot; called <code>E7.Unity</code> where anything goes. So let&apos;s name your thing, probably use <code>namespace</code> practice correctly, and put in those <code>asmdef</code> and <code>package.json</code>.</p><h3 id="if-your-plugin-sounded-epic-enough-to-be-sold-on-the-asset-store">If your plugin sounded epic enough to be sold on the Asset Store</h3><p>Put them in its own separated Unity project. Your game project has the priviledge of yoinking it directly locally by locating <code>package.json</code> inside instead of pulling from Asset Store. This way you could run your plugin&apos;s test scene in its own project, which is useful to check integrity when upgrading Unity version. Then you could &quot;dogfooding&quot; the plugin with your own game because it is linked via UPM to only you, not your customers.</p><p><a href="http://exceed7.com/native-audio/">Native Audio</a> is in its own project, which is sold on the Asset Store. In this case, you should version control the <strong>entire project </strong>because its for your private use. Then inside it <code>AssetStoreTools</code> is for submitting. Then inside a folder of your plugin could contains <code>package.json</code> and <code>asmdef</code>.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.17.23.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><h3 id="if-your-plugins-aren-t-epic-enough-to-be-sold">If your plugins aren&apos;t epic enough to be sold</h3><p>You maybe able to use GitHub integration to bring them online! The requirement is that they must be a public repo, so not for those you are going to sell. And also <code>package.json</code> must be at the root.</p><p>You can put them together in a new Unity project called &quot;My UPM Packages&quot; or similar. In this case, you should <strong>version control each folders in your <code>Assets</code> folder.</strong></p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.32.04-1.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Or you could put it in its own project. In this case don&apos;t version control from outside of <code>Assets</code> but do it inside.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.38.08.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Put <code>README.md</code> near <code>packages.json</code> and <code>asmdef</code> too. Ok, putting <code>.git</code> there may risk losing the <code>ProjectSettings</code>. But trust me GitHub integration is way cooler.</p><p>Either way, when you push them you should get a landing page with <code>package.json</code> visible from the <strong>front page</strong> together with the <code>README.md</code>. If you could get this, your GitHub package is now UPM compatible. If you put <code>.git</code> outside <code>Assets</code>, you will not get this layout.</p><p><a href="https://github.com/5argon/E7Unity">https://github.com/5argon/E7Unity</a><br><a href="https://github.com/5argon/NotchSolution">https://github.com/5argon/NotchSolution</a></p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.39.45.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.40.11.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>To pull, you must add an entry manually (by Unity 2019.1) in <code>manifest.json</code>. The format is a combination of what you put in <code>package.json</code> on the left, then URL on the right. For example :</p><pre><code>&quot;com.e7.notch-solution&quot;: &quot;git://github.com/5argon/NotchSolution.git&quot;</code></pre><p>If you want to pull again it is not automatic. Look at the bottom of <code>manifest.json</code> and remove the package&apos;s <code>lock</code>.</p><p>Not only it is online and you could share with friends, you could get contributors and pull requests! My <a href="https://github.com/5argon/NotchSolution">Notch Solution</a> which sounded epic but I decided to be not for profit ended up having multiple contributor helping me to fight the notched phone.</p><h3 id="from-now-on-starts-any-new-component-on-upm">From now on starts any new component on UPM</h3><p>To avoid this hardship of trying to <code>asmdef</code> and UPM existing game, the next &quot;module&quot; should start from UPM and link it to your main project. Great, great benefit of your internal UPM package even the one you didn&apos;t plan to share with the world includes : </p><ul><li>Occassionally you upgrade Unity or some important plugins, and now everything turns red. If you develop in UPM, you can open those <strong>individually </strong>and see if the red errors came from them or not. Keeping your sanity and let you fix part by part. Your game is now a multiple Unity project where other little project is a shell that imports just one internal UPM you have developed.</li><li>The fastest ticket to bug fixes. You can write a self contained unit tests in your UPM package. And when you suspect Unity bugged, that would make your test turn red. Usually you don&apos;t want to submit a bug report since you don&apos;t want to upload 30 GB of art assets and you got no time to make a reproduce project. But now you can submit your UPM package that would be less than 1MB of pure code, and just tell Unity team to double click on some test and see it goes red. The turnaround time will be impressively fast because they will be happy to fix this self-contained, concise problem, that they have just one goal to make the test go to green again. In summary, it will reduce the hopelessness to wait for bug fixes.</li><li>Each one has its own version control, great for time traveling and experimenting without affecting other modules.</li></ul><p>Some example where you should do this. If you are making a button in your game which plays different sound on pressing down and up, a UPM package of this maybe probably too much of a hassle. However if you want to add a &quot;stats and achievements&quot; summary screen to motivate your player in the next update? This kind of thing is ripe for a lot of bugs and you want to make sure just this section works correctly for any data thrown at it. This would be a good candidate for internal, code-only UPM.</p><h3 id="results-of-step-1-and-2">Results of Step 1 and 2</h3><p>You have cleared up stuff from your <code>Assets</code> to <code>Packages</code> down below, even if your main game hadn&apos;t used any <code>asmdef</code> yet. Save for some pesky plugins that are either dlls or immovable and must stay there, they will ended up in <code>Assembly-CSharp.dll</code> and can&apos;t be helped.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-16.51.16.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>Let&apos;s recap that we just did the green part on the right here. All your scripts are still in <code>Assembly-CSharp.dll</code> together with non-UPMable scripts, which knows <code>asmdef</code> coming from UPM and from &quot;plugin that could <code>asmdef</code> but couldn&apos;t UPM&quot; in the project, implicitly. That&apos;s why I didn&apos;t draw the arrow from <code>Assembly-CSharp.dll</code> to UPM packages, which is for explicit references.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-14.36.04-1.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>The hard work is coming next, plugins are separable by their nature. Not the same for your game scripts.</p><h2 id="step-3-determine-the-core">Step 3 : Determine the Core</h2><p>Setup your folder like this.</p><figure class="kg-card kg-image-card"><img src="https://gametorrahod.com/content/images/2019/05/Screenshot-2019-05-11-17.00.06.png" class="kg-image" alt="How to remodel your project for asmdef and UPM" loading="lazy"></figure><p>The <code>Assemblies</code> is the catching net with <code>Scripts</code> assembly, no other scripts in the project may be outside of <code>Assemblies</code>, but ideally <strong>in the end </strong>you <strong>should not </strong>let anything escaped from its own folder to this <code>Scripts</code> assembly! Instead, look at TitleScreen. Everything will be caught in its own assembly. But right now is still not the end though, you will gradually work on the transition to <code>asmdef</code> this way.</p><p>Right now only my TitleScreen falls into its <code>Title</code> assembly, and all other scenes are at the <code>Scripts</code> catch-all assembly. The <code>Scripts</code> serves one more purpose, unlike letting them go to <code>Assembly-CSharp.dll</code> this one is referencable. Even if in the end you should have nothing in <code>Scripts</code>, right now you solved one problem : your play mode test assembly could now test on <strong>all</strong> scenes by linking that <code>asmdef</code> to <code>Scripts</code>, even though you just started migrating just your title scene to <code>asmdef</code>.</p><p>The <strong>Core </strong>folder is inside this <code>Assemblies</code> folder, <strong>but you should view it as the outmost! </strong>Since we made a rule that it couldn&apos;t reference anyone, not even <code>Scripts</code>. (But it could go to plugins UPM <code>asmdef</code>)</p><p>Now, you have to solve this problem : Make this 3 <code>asmdef</code> works <code>SceneName</code> <code>Scripts</code> <code>Core</code>. You will immediately see errors if something in the <code>Core</code> goes the opposite way. So it&apos;s time to refactor your code to determine what could be in the <code>Core</code> as much as possible. And this involves &quot;the bridge problem&quot; earlier.</p><h3 id="how-to-fix-the-bridge-problem">How to fix the bridge problem</h3><p>Let the bridge contains a part of everyone instead, and everyone use this data pool. The bridge still didn&apos;t reference anyone but somehow magically knows who is going to come. Then everyone use the bridge.</p><p>For example if this problematic bridge is the <code>ChangeSceneManger</code> which was doing load scene with scene name defined in the now-separated assembly. (e.g. the manager knows how to go to Title or ModeSelect, but now the scene name are inaccessible since they go to those assembly and one way reference is preventing us.) Instead, you move just those scene&apos;s name to the bridge. It feels wrong, but fast to implement.</p><p>This is an example of mine, it has superpower to define a placeholder variable for all the scenes to write to, but no, it do not reference any of these scenes. &#xA0;It is those scene <code>asmdef</code> that should reference this bridge code. Let&apos;s say we have cheated the dependency system. Title scene knows how to look at its own section and do what it should. The bridge assembly can set things into this part without knowing what the Title assembly will do about them.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">using System;

public static class SceneOptions
{
    [Serializable]
    public struct Title
    {
        public enum TitleMode
        {
            WithoutIntro,
            WithIntro,
            SkipToModeSelect
        }
        public TitleMode titleMode;
    }

    [Serializable]
    public struct Result
    {
        int p1Score;
        int p2Score;
        bool challengeCleared;
        bool withoutScore;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>It is a price to pay for remodeling a project for <code>asmdef</code> + UPM deep into the project.</p>]]></content:encoded></item></channel></rss>