<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://matthewminer.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://matthewminer.com/" rel="alternate" type="text/html" /><updated>2026-03-08T19:03:00+00:00</updated><id>https://matthewminer.com/feed.xml</id><title type="html">Matthew Miner</title><subtitle>Software Engineer and Game Developer</subtitle><author><name>Matthew Miner</name></author><entry><title type="html">Thirteen Photos of Street Art From Puerto Vallarta, Plus a Crocodile</title><link href="https://matthewminer.com/2026/03/08/thirteen-photos-of-street-art-from-puerto-vallarta" rel="alternate" type="text/html" title="Thirteen Photos of Street Art From Puerto Vallarta, Plus a Crocodile" /><published>2026-03-08T00:00:00+00:00</published><updated>2026-03-08T00:00:00+00:00</updated><id>https://matthewminer.com/2026/03/08/thirteen-photos-of-street-art-from-puerto-vallarta</id><content type="html" xml:base="https://matthewminer.com/2026/03/08/thirteen-photos-of-street-art-from-puerto-vallarta"><![CDATA[<p>Puerto Vallarta, Mexico is a beautiful place. We got stranded for three days when cartel goons started <a href="https://www.nytimes.com/2026/02/23/world/americas/peurto-vallarta-tourists-cartel.html">burning cars and the airport shut down</a>, but otherwise it was a great trip. Lots to see and do — beaches and tacos and iguanas — and photo ops galore. Here are a few shots from Zona Romántica and Bucerías to the north.</p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-01.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-01-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-02.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-02-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-03.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-03-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-04.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-04-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-05.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-05-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-06.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-06-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-07.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-07-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-08.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-08-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-09.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-09-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-10.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-10-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-11.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-11-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-12.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-12-thumbnail.jpg" alt="Street art" />
</a></p>

<p><a class="image-link" href="/images/puerto-vallarta/street-art-13.jpg" target="_blank">
    <img src="/images/puerto-vallarta/street-art-13-thumbnail.jpg" alt="Street art" />
</a></p>

<p>And here’s the promised crocodile. Our room overlooked a river, and we often saw this chap relaxing in the mud. I understand why the hotel built that gate.</p>

<p><a class="image-link" href="/images/puerto-vallarta/crocodile.jpg" target="_blank">
    <img src="/images/puerto-vallarta/crocodile-thumbnail.jpg" alt="Crocodile" />
</a></p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[Puerto Vallarta, Mexico is a beautiful place. We got stranded for three days when cartel goons started burning cars and the airport shut down, but otherwise it was a great trip. Lots to see and do — beaches and tacos and iguanas — and photo ops galore. Here are a few shots from Zona Romántica and Bucerías to the north.]]></summary></entry><entry><title type="html">The Count of Monte Cristo</title><link href="https://matthewminer.com/2025/06/24/the-count-of-monte-cristo" rel="alternate" type="text/html" title="The Count of Monte Cristo" /><published>2025-06-24T00:00:00+00:00</published><updated>2025-06-24T00:00:00+00:00</updated><id>https://matthewminer.com/2025/06/24/the-count-of-monte-cristo</id><content type="html" xml:base="https://matthewminer.com/2025/06/24/the-count-of-monte-cristo"><![CDATA[<p>After an eternity I finished reading <em>The Count of Monte Cristo</em>. It’s a lengthy tale. It contains everything a novel should: a daring prison escape, secret identities, hidden treasure, duels, revenge, murder, endless gossip among the fashionable elites of Paris.</p>

<p><img src="/images/the-count-of-monte-cristo.jpg" alt="The Count of Monte Cristo book spine" /></p>

<p>The protagonist starts a humble seafarer but becomes superhuman, accomplished at everything from marksmanship to disguises to toxicology. But my favourite superpower? Punctuality.</p>

<blockquote>
  <p>“I warn you, I am fearfully punctual.”</p>
</blockquote>

<blockquote>
  <p>“The hand of your clock will not be more punctual than I.”</p>
</blockquote>

<blockquote>
  <p>“Punctuality,” said Monte Cristo, “is the politeness of kings.”</p>
</blockquote>

<p>I bet that showing up on time was no easy feat in the nineteenth century, what with mechanical watches you had to wind daily and horses your most reliable mode of transportation. It helps that Monte Cristo has sweet horses.</p>

<blockquote>
  <p>Meanwhile the count had arrived at his house. The journey had taken him six minutes, and these six minutes had been enough for him to be seen by twenty young men who, recognizing the cost of a team that was well beyond their means, had spurred their mounts to a gallop so that they could catch a glimpse of this noble lord who paid ten thousand francs apiece for his horses.</p>
</blockquote>

<p>Dumas writes about expensive horses and carriages the way we talk about Lamborghinis and Cybertrucks. Coupé, landau, phaeton, barouche, post-chaise — I don’t know what any of these are, but I appreciate that he assumes I do.</p>

<p>For <em>Monte Cristo</em> superfans, Wikipedia lists 14 TV shows and 30 movies in the <abbr title="Monte Cristo Universe">MCU</abbr> (Monte Cristo Universe). I watched three: the 2002 American version; the 2024 French epic; and <em>Wishbone</em> episode 27, “The Count’s Account”. All are entertaining, but <em>Wishbone</em> is the winner. It crams a book I spent months reading into a half hour episode, complete with a parallel plot about a dipshit bully who ruins a lady’s garden to frame the neighbourhood nerd and that nerd’s subsequent revenge.</p>

<p>You know what each adaptation is missing though? The count’s punctuality. Edmond Dantès’ most formidable superpower, not mentioned a single time. C’mon Hollywood.</p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[After an eternity I finished reading The Count of Monte Cristo. It’s a lengthy tale. It contains everything a novel should: a daring prison escape, secret identities, hidden treasure, duels, revenge, murder, endless gossip among the fashionable elites of Paris.]]></summary></entry><entry><title type="html">Unity Extension Methods</title><link href="https://matthewminer.com/2025/06/13/unity-extension-methods" rel="alternate" type="text/html" title="Unity Extension Methods" /><published>2025-06-13T00:00:00+00:00</published><updated>2025-06-13T00:00:00+00:00</updated><id>https://matthewminer.com/2025/06/13/unity-extension-methods</id><content type="html" xml:base="https://matthewminer.com/2025/06/13/unity-extension-methods"><![CDATA[<p>Speaking of <a href="/2025/06/08/a-slightly-nicer-api-for-unity's-nonalloc-physics-functions">Unity packages</a>, here’s one I add to every new project: <a href="https://github.com/mminer/unity-extensions">Unity Extensions</a>. Whenever I find myself wishing an API existed for a <code class="language-plaintext highlighter-rouge">UnityEngine</code> type, I add it to this package as an extension method. The result is a small collection of utilities that make working with <code class="language-plaintext highlighter-rouge">Transform</code>, <code class="language-plaintext highlighter-rouge">Vector3</code>, and so forth more pleasant.</p>

<p>Say you want to set the Y position of your player to zero. You might write this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transform</span><span class="p">.</span><span class="n">position</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Vector3</span><span class="p">(</span><span class="n">transform</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">transform</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">z</span><span class="p">);</span>

<span class="c1">// Or:</span>
<span class="kt">var</span> <span class="n">position</span> <span class="p">=</span> <span class="n">transform</span><span class="p">.</span><span class="n">position</span><span class="p">;</span>
<span class="n">position</span><span class="p">.</span><span class="n">y</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="n">transform</span><span class="p">.</span><span class="n">position</span> <span class="p">=</span> <span class="n">position</span>
</code></pre></div></div>

<p>With the <code class="language-plaintext highlighter-rouge">Transform.SetY</code> extension method, you can write this instead:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transform</span><span class="p">.</span><span class="nf">SetY</span><span class="p">(</span><span class="m">0</span><span class="p">);</span>
</code></pre></div></div>

<p>Another example. Given a list of enemy positions, you want to find the one closest to your player. Not difficult, but you may appreciate this short version:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">closestEnemyPosition</span> <span class="p">=</span> <span class="n">transform</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="nf">GetClosest</span><span class="p">(</span><span class="n">enemyPositions</span><span class="p">);</span>
</code></pre></div></div>

<p>Take a gander at the
<a href="https://github.com/mminer/unity-extensions?tab=readme-ov-file#extensions">README</a> for a full list of extension methods.</p>

<p>Exciting? No. Useful? You bet.</p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[Speaking of Unity packages, here’s one I add to every new project: Unity Extensions. Whenever I find myself wishing an API existed for a UnityEngine type, I add it to this package as an extension method. The result is a small collection of utilities that make working with Transform, Vector3, and so forth more pleasant.]]></summary></entry><entry><title type="html">A Slightly Nicer API for Unity’s NonAlloc Physics Functions</title><link href="https://matthewminer.com/2025/06/08/a-slightly-nicer-api-for-unity's-nonalloc-physics-functions" rel="alternate" type="text/html" title="A Slightly Nicer API for Unity’s NonAlloc Physics Functions" /><published>2025-06-08T00:00:00+00:00</published><updated>2025-06-08T00:00:00+00:00</updated><id>https://matthewminer.com/2025/06/08/a-slightly-nicer-api-for-unity&apos;s-nonalloc-physics-functions</id><content type="html" xml:base="https://matthewminer.com/2025/06/08/a-slightly-nicer-api-for-unity&apos;s-nonalloc-physics-functions"><![CDATA[<p>If you’re a Unity developer, you might use the allocation-free versions of its intersection functions — <code class="language-plaintext highlighter-rouge">Physics.RaycastNonAlloc</code> instead of <code class="language-plaintext highlighter-rouge">Physics.RaycastAll</code>, <code class="language-plaintext highlighter-rouge">Physics.OverlapBoxNonAlloc</code> instead of <code class="language-plaintext highlighter-rouge">Physics.OverlapBox</code>, and so forth. And if you don’t, perhaps you ought to; they’re an easy way to reduce garbage collection overhead in your game if you call these functions every frame.</p>

<p>A typical implementation looks like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">RaycastHit</span><span class="p">[]</span> <span class="n">hits</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RaycastHit</span><span class="p">[</span><span class="m">10</span><span class="p">];</span>

<span class="k">void</span> <span class="nf">Update</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">hitCount</span> <span class="p">=</span> <span class="n">Physics</span><span class="p">.</span><span class="nf">RaycastNonAlloc</span><span class="p">(</span><span class="n">transform</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">transform</span><span class="p">.</span><span class="n">forward</span><span class="p">,</span> <span class="n">hits</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">hitCount</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">hit</span> <span class="p">=</span> <span class="n">hits</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span><span class="s">"Hit: "</span> <span class="p">+</span> <span class="n">hit</span><span class="p">.</span><span class="n">collider</span><span class="p">.</span><span class="n">name</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you write this pattern often, a useful abstraction is to wrap the logic in a class and use <a href="https://learn.microsoft.com/en-us/dotnet/api/system.readonlyspan-1"><code class="language-plaintext highlighter-rouge">ReadOnlySpan</code></a> to expose the results.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Raycaster</span>
<span class="p">{</span>
    <span class="n">RaycastHit</span><span class="p">[]</span> <span class="n">hits</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RaycastHit</span><span class="p">[</span><span class="m">10</span><span class="p">];</span>

    <span class="n">ReadOnlySpan</span><span class="p">&lt;</span><span class="n">RaycastHit</span><span class="p">&gt;</span> <span class="nf">RaycastAll</span><span class="p">(</span><span class="n">Vector3</span> <span class="n">origin</span><span class="p">,</span> <span class="n">Vector3</span> <span class="n">direction</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">hitCount</span> <span class="p">=</span> <span class="n">Physics</span><span class="p">.</span><span class="nf">RaycastNonAlloc</span><span class="p">(</span><span class="n">origin</span><span class="p">,</span> <span class="n">direction</span><span class="p">,</span> <span class="n">hits</span><span class="p">);</span>
        <span class="k">return</span> <span class="k">new</span> <span class="n">ReadOnlySpan</span><span class="p">&lt;</span><span class="n">RaycastHit</span><span class="p">&gt;(</span><span class="n">hits</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">hitCount</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ReadOnlySpan</code> is a lightweight container that allows you to peek into a slice of a managed array. With this new class, we can now iterate over our raycast hits in a foreach loop; no need to manage a results array and no danger of misusing <code class="language-plaintext highlighter-rouge">hitCount</code>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Raycaster</span> <span class="n">raycaster</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>

<span class="k">void</span> <span class="nf">Update</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">hits</span> <span class="p">=</span> <span class="n">raycaster</span><span class="p">.</span><span class="nf">RaycastAll</span><span class="p">(</span><span class="n">transform</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">transform</span><span class="p">.</span><span class="n">forward</span><span class="p">);</span>

    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">hit</span> <span class="k">in</span> <span class="n">hits</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span><span class="s">"Hit: "</span> <span class="p">+</span> <span class="n">hit</span><span class="p">.</span><span class="n">collider</span><span class="p">.</span><span class="n">name</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Simple as it is, I use this pattern enough that it was worthwhile to codify into a reusable package. After adding <a href="https://github.com/mminer/nonalloc-physics-wrapper">nonalloc-physics-wrapper</a> to your game, prefix any <code class="language-plaintext highlighter-rouge">Physics</code> intersection function with <code class="language-plaintext highlighter-rouge">NonAlloc</code> and iterate over the return value as before.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">- var hits = Physics.RaycastAll(ray);
</span><span class="gi">+ var hits = NonAllocPhysics.RaycastAll(ray);
</span>
- var colliders = Physics.OverlapBox(center, halfExtents);
<span class="gi">+ var colliders = NonAllocPhysics.OverlapBox(center, halfExtents);
</span>
... and so on.
</code></pre></div></div>

<p>Now instead of a new array being allocated on each call, a single one is reused across multiple calls and the garbage collector has that much less work to do. He deserves a break.</p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[If you’re a Unity developer, you might use the allocation-free versions of its intersection functions — Physics.RaycastNonAlloc instead of Physics.RaycastAll, Physics.OverlapBoxNonAlloc instead of Physics.OverlapBox, and so forth. And if you don’t, perhaps you ought to; they’re an easy way to reduce garbage collection overhead in your game if you call these functions every frame.]]></summary></entry><entry><title type="html">Unity Menu: A macOS Menubar App</title><link href="https://matthewminer.com/2025/05/26/unity-menu-a-macos-menubar-app" rel="alternate" type="text/html" title="Unity Menu: A macOS Menubar App" /><published>2025-05-26T00:00:00+00:00</published><updated>2025-05-26T00:00:00+00:00</updated><id>https://matthewminer.com/2025/05/26/unity-menu-a-macos-menubar-app</id><content type="html" xml:base="https://matthewminer.com/2025/05/26/unity-menu-a-macos-menubar-app"><![CDATA[<p><em><abbr title="Too Long; Didn't Read">TL;DR</abbr>: I built a macOS menubar app to quickly open running Unity projects. <a href="https://github.com/mminer/unity-menu/releases">Download here</a>.</em></p>

<hr />

<p>In my day job I juggle multiple Unity projects, regularly flipping between 3–5 of them. Neither the macOS Dock nor the app switcher make it clear which project I’m switching to.</p>

<p><img src="/images/unity-dock-icons.png" alt="Unity Dock icons" /></p>

<p>Will I choose the correct one? It’s a fun little game.</p>

<p>Unity Hub isn’t better. It can launch a project, but try to open one that’s running and I get a “Project is already open” message.</p>

<p><img src="/images/unity-hub-project-already-open.png" alt="Unity Hub &quot;Project is already open&quot;" /></p>

<p>To take this guesswork out of switching between Unity instances, I put together a macOS app that lists open projects in the menubar. Click a project to bring it to the front.</p>

<p><img src="/images/unity-menu.png" alt="Unity Menu" /></p>

<p>With the help of a global shortcut that opens the menu, activating the Unity process I want is 42% faster. Scientifically measured, peer review unnecessary.</p>

<p><img src="/images/unity-menu-global-keyboard-shortcut.png" alt="Unity Menu global keyboard shortcut" /></p>

<p>I also built a <code class="language-plaintext highlighter-rouge">unity-menu</code> command line tool that does the same thing as the menubar app.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ unity-menu
[1] Grand Theft Auto 6
[2] Huedini
[3] Orbert
[4] Time Squatch
Selection: 3
</code></pre></div></div>

<p>I rarely employ the <abbr title="Command Line Interface">CLI</abbr> version, but I have a notion to write <a href="https://www.obdev.at/products/launchbar/index.html">Launchbar</a> and <a href="https://www.alfredapp.com">Alfred</a> actions using it. Work in progress.</p>

<p>I developed this to scratch my own itch, but if it’s an itch you also wish to scratch, find the source code and app download in <a href="https://github.com/mminer/unity-menu">this GitHub repo</a>.</p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[TL;DR: I built a macOS menubar app to quickly open running Unity projects. Download here.]]></summary></entry><entry><title type="html">Scammers Be Scamming</title><link href="https://matthewminer.com/2025/03/16/scammers-be-scamming" rel="alternate" type="text/html" title="Scammers Be Scamming" /><published>2025-03-16T00:00:00+00:00</published><updated>2025-03-16T00:00:00+00:00</updated><id>https://matthewminer.com/2025/03/16/scammers-be-scamming</id><content type="html" xml:base="https://matthewminer.com/2025/03/16/scammers-be-scamming"><![CDATA[<p>Here’s a scam I encountered selling my beloved copy of <em>Frozen</em>:<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<ol>
  <li>I listed the Blu-ray for sale on Facebook Marketplace.</li>
  <li>“Ernesto” asked to buy it. He was out of town until Monday, but he kindly offered to pay upfront if I promised to hold it for him.</li>
  <li>I agreed.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> Within minutes I received an e-transfer email.</li>
  <li>I clicked the link to deposit the cash, chose my bank, and entered my account details in the login form.</li>
  <li>I lost all my money.</li>
</ol>

<p>I stopped before step 4, but I can see how people fall for this. Here’s the email:</p>

<p><img src="/images/facebook-marketplace-scam-email.png" alt="Facebook Marketplace scam email" /></p>

<p>If you’re Canadian you’ll have received e-transfer emails like this. There are a few oddities, like the position of ™ at the front of the subject line and the dubious shine@twce.org.tw sender, but otherwise this looks genuine.</p>

<p>Ditto for the bank website that opened when I clicked “Receive Money”; it was a dead ringer for the real deal. The only giveaways were the <span style="white-space: nowrap;">e-interac-88416061.1842904.icu</span> domain name (I was unaware .icu is a valid TLD), my browser’s refusal to autofill my credentials, and style differences that only those with a photographic memory of their bank’s website will notice. Oh, and the unavailability of auto-deposit that forced me to login in the first place. Ernesto explained:</p>

<p><img src="/images/facebook-marketplace-scam-auto-deposit.png" alt="Facebook Marketplace scam auto-deposit-message" /></p>

<p>Hmm.</p>

<p>I’m curious what would happen if I entered my username and password. Does Ernesto sign in immediately and withdraw my life savings, or does he collect credentials from a bunch of suckers to sell in bulk? I assume (hope) my bank triggers a 2FA challenge the moment it sees a login attempt from a new IP. Ernesto, if you’re reading this, enlighten me.</p>

<p>I gather this is a common racket, because when I listed another item for sale, I received the same offer. At this rate I’m never going to offload my physical media collection.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Do you want the 2-disc Blu-ray + DVD collector’s edition of <em>Frozen</em>? It’s still for sale. Cash only. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Any offer to buy is welcome after the deluge of “Hi, is this available?” messages I receive after any new listing. Without fail these inquiries result in radio silence when I confirm that yes, it’s available. Why do people do this? Facebook Marketplace is weird. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[Here’s a scam I encountered selling my beloved copy of Frozen:1 Do you want the 2-disc Blu-ray + DVD collector’s edition of Frozen? It’s still for sale. Cash only. &#8617;]]></summary></entry><entry><title type="html">Unity Position Visualizer</title><link href="https://matthewminer.com/2025/03/15/unity-position-visualizer" rel="alternate" type="text/html" title="Unity Position Visualizer" /><published>2025-03-15T00:00:00+00:00</published><updated>2025-03-15T00:00:00+00:00</updated><id>https://matthewminer.com/2025/03/15/unity-position-visualizer</id><content type="html" xml:base="https://matthewminer.com/2025/03/15/unity-position-visualizer"><![CDATA[<p>In my day job I often need to visualize positions in Unity. Given a coordinate, I might drop a cube in the scene to serve as a visual marker, but it’s a clunky workflow. Here’s <a href="https://github.com/mminer/position-visualizer">an editor tool</a> I built to make this easier. I find it useful; maybe you will too.</p>

<video autoplay="" height="417" loop="" src="/videos/position-visualizer.mp4" width="660"></video>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[In my day job I often need to visualize positions in Unity. Given a coordinate, I might drop a cube in the scene to serve as a visual marker, but it’s a clunky workflow. Here’s an editor tool I built to make this easier. I find it useful; maybe you will too.]]></summary></entry><entry><title type="html">Thirteen Photos From Maui</title><link href="https://matthewminer.com/2024/11/11/thirteen-photos-from-maui" rel="alternate" type="text/html" title="Thirteen Photos From Maui" /><published>2024-11-11T00:00:00+00:00</published><updated>2024-11-11T00:00:00+00:00</updated><id>https://matthewminer.com/2024/11/11/thirteen-photos-from-maui</id><content type="html" xml:base="https://matthewminer.com/2024/11/11/thirteen-photos-from-maui"><![CDATA[<p>Beaches, waterfalls, and rainbows, oh my.</p>

<p><a class="image-link" href="/images/maui/coastline.jpg" target="_blank">
    <img src="/images/maui/coastline-thumbnail.jpg" alt="Coastline" />
</a></p>

<p><a class="image-link" href="/images/maui/waterfall.jpg" target="_blank">
    <img src="/images/maui/waterfall-thumbnail.jpg" alt="Waterfall" />
</a></p>

<p><a class="image-link" href="/images/maui/hookipa-rainbow.jpg" target="_blank">
    <img src="/images/maui/hookipa-rainbow-thumbnail.jpg" alt="Ho'okipa rainbow" />
</a></p>

<p>Maui packs a lot of geology into one island. A short drive takes you from lush valleys to parched volcanoes.</p>

<p><a class="image-link" href="/images/maui/waihee-ridge.jpg" target="_blank">
    <img src="/images/maui/waihee-ridge-thumbnail.jpg" alt="Waihee Ridge" />
</a></p>

<p><a class="image-link" href="/images/maui/haleakala.jpg" target="_blank">
    <img src="/images/maui/haleakala-thumbnail.jpg" alt="Haleakalā" />
</a></p>

<p>Haleakalā is one of the <a href="https://www.npr.org/2022/08/25/1119484767/experience-the-quietest-place-on-earth">quietest places on Earth</a>. I only learned this later, but I noticed it immediately as we descended into the crater. No traffic, no wind, no wildlife. It’s eerie.</p>

<p><a class="image-link" href="/images/maui/haleakala-sunset.jpg" target="_blank">
    <img src="/images/maui/haleakala-sunset-thumbnail.jpg" alt="Haleakalā sunset" />
</a></p>

<p><a class="image-link" href="/images/maui/iao-valley.jpg" target="_blank">
    <img src="/images/maui/iao-valley-thumbnail.jpg" alt="ʻĪao Valley" />
</a></p>

<p><a class="image-link" href="/images/maui/west-maui.jpg" target="_blank">
    <img src="/images/maui/west-maui-thumbnail.jpg" alt="West Maui" />
</a></p>

<p><a class="image-link" href="/images/maui/waianapanapa.jpg" target="_blank">
    <img src="/images/maui/waianapanapa-thumbnail.jpg" alt="Waiʻānapanapa" />
</a></p>

<p><a class="image-link" href="/images/maui/lanai.jpg" target="_blank">
    <img src="/images/maui/lanai-thumbnail.jpg" alt="Lanai" />
</a></p>

<p>The sea turtles were my favourite. I didn’t expect to see so many. A few swam past me as I was snorkelling, other times we’d find them basking on the beach, unperturbed by the humans walking by.</p>

<p><a class="image-link" href="/images/maui/sea-turtle.jpg" target="_blank">
    <img src="/images/maui/sea-turtle-thumbnail.jpg" alt="Sea turtle" />
</a></p>

<p><a class="image-link" href="/images/maui/sea-turtle-duo.jpg" target="_blank">
    <img src="/images/maui/sea-turtle-duo-thumbnail.jpg" alt="Sea turtle duo" />
</a></p>

<p>These guys are living the life.</p>

<p><a class="image-link" href="/images/maui/kihei-sunset.jpg" target="_blank">
    <img src="/images/maui/kihei-sunset-thumbnail.jpg" alt="Kihei sunset" />
</a></p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[Beaches, waterfalls, and rainbows, oh my.]]></summary></entry><entry><title type="html">2D Flocking in Unity</title><link href="https://matthewminer.com/2024/11/03/2d-flocking-in-unity" rel="alternate" type="text/html" title="2D Flocking in Unity" /><published>2024-11-03T00:00:00+00:00</published><updated>2024-11-03T00:00:00+00:00</updated><id>https://matthewminer.com/2024/11/03/2d-flocking-in-unity</id><content type="html" xml:base="https://matthewminer.com/2024/11/03/2d-flocking-in-unity"><![CDATA[<p><a href="https://en.wikipedia.org/wiki/Flocking">Flocking</a> implementations are plentiful. Here’s mine.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<video autoplay="" height="413" loop="" src="/videos/flocking.mp4" width="660"></video>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">UnityEngine</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">Flock</span> <span class="p">:</span> <span class="n">MonoBehaviour</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Min</span><span class="p">(</span><span class="m">0</span><span class="p">)]</span> <span class="kt">float</span> <span class="n">spawnRadius</span> <span class="p">=</span> <span class="m">5f</span><span class="p">;</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Min</span><span class="p">(</span><span class="m">1</span><span class="p">)]</span> <span class="kt">int</span> <span class="n">unitCount</span> <span class="p">=</span> <span class="m">50</span><span class="p">;</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">]</span> <span class="n">Transform</span> <span class="n">unitPrefab</span><span class="p">;</span>

    <span class="p">[</span><span class="nf">Header</span><span class="p">(</span><span class="s">"Unit"</span><span class="p">)]</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Range</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">360</span><span class="p">)]</span> <span class="kt">float</span> <span class="n">fieldOfView</span> <span class="p">=</span> <span class="m">270f</span><span class="p">;</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Min</span><span class="p">(</span><span class="m">0</span><span class="p">)]</span> <span class="kt">float</span> <span class="n">separationRadius</span> <span class="p">=</span> <span class="m">1f</span><span class="p">;</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Min</span><span class="p">(</span><span class="m">0</span><span class="p">)]</span> <span class="kt">float</span> <span class="n">sightRadius</span> <span class="p">=</span> <span class="m">3f</span><span class="p">;</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Min</span><span class="p">(</span><span class="m">0</span><span class="p">)]</span> <span class="kt">float</span> <span class="n">speed</span> <span class="p">=</span> <span class="m">1f</span><span class="p">;</span>
    <span class="p">[</span><span class="n">SerializeField</span><span class="p">,</span> <span class="nf">Min</span><span class="p">(</span><span class="m">0</span><span class="p">)]</span> <span class="kt">float</span> <span class="n">steeringForce</span> <span class="p">=</span> <span class="m">1f</span><span class="p">;</span>

    <span class="n">Transform</span><span class="p">[]</span> <span class="n">units</span><span class="p">;</span>

    <span class="k">void</span> <span class="nf">Awake</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="nf">InstantiateUnits</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">void</span> <span class="nf">Update</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">unit</span> <span class="k">in</span> <span class="n">units</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nf">UpdateUnit</span><span class="p">(</span><span class="n">unit</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">void</span> <span class="nf">InstantiateUnits</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">units</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Transform</span><span class="p">[</span><span class="n">unitCount</span><span class="p">];</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">unitCount</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">position</span> <span class="p">=</span> <span class="n">Random</span><span class="p">.</span><span class="n">insideUnitCircle</span> <span class="p">*</span> <span class="n">spawnRadius</span><span class="p">;</span>
            <span class="kt">var</span> <span class="n">rotation</span> <span class="p">=</span> <span class="n">Quaternion</span><span class="p">.</span><span class="nf">Euler</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">Random</span><span class="p">.</span><span class="nf">Range</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">360</span><span class="p">));</span>
            <span class="n">units</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="nf">Instantiate</span><span class="p">(</span><span class="n">unitPrefab</span><span class="p">,</span> <span class="n">position</span><span class="p">,</span> <span class="n">rotation</span><span class="p">,</span> <span class="n">transform</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">void</span> <span class="nf">UpdateUnit</span><span class="p">(</span><span class="n">Transform</span> <span class="n">unit</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">unitPosition</span> <span class="p">=</span> <span class="p">(</span><span class="n">Vector2</span><span class="p">)</span><span class="n">unit</span><span class="p">.</span><span class="n">position</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">unitDirection</span> <span class="p">=</span> <span class="p">(</span><span class="n">Vector2</span><span class="p">)</span><span class="n">unit</span><span class="p">.</span><span class="n">up</span><span class="p">;</span>

        <span class="kt">var</span> <span class="n">neighborCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">averageNeighborPosition</span> <span class="p">=</span> <span class="n">Vector2</span><span class="p">.</span><span class="n">zero</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">averageNeighborDirection</span> <span class="p">=</span> <span class="n">Vector2</span><span class="p">.</span><span class="n">zero</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">separation</span> <span class="p">=</span> <span class="n">Vector2</span><span class="p">.</span><span class="n">zero</span><span class="p">;</span>

        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">otherUnit</span> <span class="k">in</span> <span class="n">units</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">otherUnit</span> <span class="p">==</span> <span class="n">unit</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">continue</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="kt">var</span> <span class="n">otherUnitPosition</span> <span class="p">=</span> <span class="p">(</span><span class="n">Vector2</span><span class="p">)</span><span class="n">otherUnit</span><span class="p">.</span><span class="n">position</span><span class="p">;</span>
            <span class="kt">var</span> <span class="n">otherUnitDirection</span> <span class="p">=</span> <span class="p">(</span><span class="n">Vector2</span><span class="p">)</span><span class="n">otherUnit</span><span class="p">.</span><span class="n">up</span><span class="p">;</span>

            <span class="kt">var</span> <span class="n">directionToOther</span> <span class="p">=</span> <span class="n">otherUnitPosition</span> <span class="p">-</span> <span class="n">unitPosition</span><span class="p">;</span>
            <span class="kt">var</span> <span class="n">sqrDistanceToOther</span> <span class="p">=</span> <span class="n">directionToOther</span><span class="p">.</span><span class="n">sqrMagnitude</span><span class="p">;</span>
            <span class="kt">var</span> <span class="n">isWithinSightRadius</span> <span class="p">=</span> <span class="n">sqrDistanceToOther</span> <span class="p">&lt;=</span> <span class="n">sightRadius</span> <span class="p">*</span> <span class="n">sightRadius</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(!</span><span class="n">isWithinSightRadius</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">continue</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="kt">var</span> <span class="n">angleToOther</span> <span class="p">=</span> <span class="n">Vector2</span><span class="p">.</span><span class="nf">Angle</span><span class="p">(</span><span class="n">unitDirection</span><span class="p">,</span> <span class="n">directionToOther</span><span class="p">);</span>
            <span class="kt">var</span> <span class="n">isWithinFieldOfView</span> <span class="p">=</span> <span class="n">angleToOther</span> <span class="p">&lt;=</span> <span class="n">fieldOfView</span> <span class="p">/</span> <span class="m">2</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(!</span><span class="n">isWithinFieldOfView</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">continue</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="n">neighborCount</span><span class="p">++;</span>
            <span class="n">averageNeighborPosition</span> <span class="p">+=</span> <span class="n">otherUnitPosition</span><span class="p">;</span>
            <span class="n">averageNeighborDirection</span> <span class="p">+=</span> <span class="n">otherUnitDirection</span><span class="p">;</span>

            <span class="kt">var</span> <span class="n">isWithinSeparationRadius</span> <span class="p">=</span> <span class="n">sqrDistanceToOther</span> <span class="p">&lt;=</span> <span class="n">separationRadius</span> <span class="p">*</span> <span class="n">separationRadius</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">isWithinSeparationRadius</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">separation</span> <span class="p">-=</span> <span class="n">directionToOther</span> <span class="p">*</span> <span class="n">separationRadius</span> <span class="p">/</span> <span class="n">sqrDistanceToOther</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="n">Debug</span><span class="p">.</span><span class="nf">DrawLine</span><span class="p">(</span><span class="n">unitPosition</span><span class="p">,</span> <span class="n">otherUnitPosition</span><span class="p">,</span> <span class="n">Color</span><span class="p">.</span><span class="n">green</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">neighborCount</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">averageNeighborPosition</span> <span class="p">/=</span> <span class="n">neighborCount</span><span class="p">;</span>
            <span class="n">averageNeighborDirection</span> <span class="p">/=</span> <span class="n">neighborCount</span><span class="p">;</span>

            <span class="kt">var</span> <span class="n">cohesion</span> <span class="p">=</span> <span class="p">(</span><span class="n">averageNeighborPosition</span> <span class="p">-</span> <span class="n">unitPosition</span><span class="p">).</span><span class="n">normalized</span><span class="p">;</span>
            <span class="kt">var</span> <span class="n">alignment</span> <span class="p">=</span> <span class="n">averageNeighborDirection</span><span class="p">.</span><span class="n">normalized</span><span class="p">;</span>
            <span class="n">unit</span><span class="p">.</span><span class="n">up</span> <span class="p">=</span> <span class="n">Vector3</span><span class="p">.</span><span class="nf">Lerp</span><span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">up</span><span class="p">,</span> <span class="n">cohesion</span> <span class="p">+</span> <span class="n">alignment</span> <span class="p">+</span> <span class="n">separation</span><span class="p">,</span> <span class="n">steeringForce</span> <span class="p">*</span> <span class="n">Time</span><span class="p">.</span><span class="n">deltaTime</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="n">unit</span><span class="p">.</span><span class="n">position</span> <span class="p">+=</span> <span class="n">unit</span><span class="p">.</span><span class="n">up</span> <span class="p">*</span> <span class="p">(</span><span class="n">speed</span> <span class="p">*</span> <span class="n">Time</span><span class="p">.</span><span class="n">deltaTime</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here’s an <code class="language-plaintext highlighter-rouge">OnDrawGizmos</code> function to visualize the field of view and other parameters.</p>

<video autoplay="" height="413" loop="" src="/videos/flocking-gizmos.mp4" width="660"></video>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="nf">OnDrawGizmos</span><span class="p">()</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">units</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">Handles</span><span class="p">.</span><span class="n">color</span> <span class="p">=</span> <span class="n">Color</span><span class="p">.</span><span class="n">red</span><span class="p">;</span>

    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">unit</span> <span class="k">in</span> <span class="n">units</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Handles</span><span class="p">.</span><span class="nf">DrawLine</span><span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">unit</span><span class="p">.</span><span class="n">position</span> <span class="p">+</span> <span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">up</span> <span class="p">*</span> <span class="n">sightRadius</span><span class="p">));</span>

        <span class="kt">var</span> <span class="n">arcStartDirection</span> <span class="p">=</span> <span class="n">Quaternion</span><span class="p">.</span><span class="nf">Euler</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="p">-</span><span class="n">fieldOfView</span> <span class="p">/</span> <span class="m">2</span><span class="p">)</span> <span class="p">*</span> <span class="n">unit</span><span class="p">.</span><span class="n">up</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">arcEndDirection</span> <span class="p">=</span> <span class="n">Quaternion</span><span class="p">.</span><span class="nf">Euler</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">fieldOfView</span> <span class="p">/</span> <span class="m">2</span><span class="p">)</span> <span class="p">*</span> <span class="n">unit</span><span class="p">.</span><span class="n">up</span><span class="p">;</span>
        <span class="n">Handles</span><span class="p">.</span><span class="nf">DrawWireArc</span><span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">Vector3</span><span class="p">.</span><span class="n">forward</span><span class="p">,</span> <span class="n">arcStartDirection</span><span class="p">,</span> <span class="n">fieldOfView</span><span class="p">,</span> <span class="n">separationRadius</span><span class="p">);</span>
        <span class="n">Handles</span><span class="p">.</span><span class="nf">DrawWireArc</span><span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">Vector3</span><span class="p">.</span><span class="n">forward</span><span class="p">,</span> <span class="n">arcStartDirection</span><span class="p">,</span> <span class="n">fieldOfView</span><span class="p">,</span> <span class="n">sightRadius</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">arcStartPosition</span> <span class="p">=</span> <span class="n">unit</span><span class="p">.</span><span class="n">position</span> <span class="p">+</span> <span class="p">(</span><span class="n">arcStartDirection</span> <span class="p">*</span> <span class="n">sightRadius</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">arcEndPosition</span> <span class="p">=</span> <span class="n">unit</span><span class="p">.</span><span class="n">position</span> <span class="p">+</span> <span class="p">(</span><span class="n">arcEndDirection</span> <span class="p">*</span> <span class="n">sightRadius</span><span class="p">);</span>
        <span class="n">Handles</span><span class="p">.</span><span class="nf">DrawLine</span><span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">arcStartPosition</span><span class="p">);</span>
        <span class="n">Handles</span><span class="p">.</span><span class="nf">DrawLine</span><span class="p">(</span><span class="n">unit</span><span class="p">.</span><span class="n">position</span><span class="p">,</span> <span class="n">arcEndPosition</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To write my version I followed along with <a href="https://www.oreilly.com/library/view/ai-for-game/0596005555/"><em>AI for Game Developers</em></a>. At 20 years old it’s a senior citizen of a tech reference, but it explains the algorithm well.</p>

<p>Performance degrades quickly as you increase the number of units. Spawn 1,000 units and watch the frame rate plummet. The algorithm is an ideal candidate for Unity’s job system to split the work across multiple threads, but jobs (and multithreading generally) are unsupported on WebGL, the platform I’m targeting. Alas.</p>

<p>A better approach is to reduce the number of neighbour checks. The Internet tells me that with spatial partitioning we can knock the complexity from <em>O</em>(<em>n</em><sup>2</sup>) down to <em>O</em>(<em>n</em>). A fine next step.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Complete code <a href="https://gist.github.com/mminer/227ce5f4eaaf70d382e6823118ae031f">on GitHub</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[Flocking implementations are plentiful. Here’s mine.1 Complete code on GitHub. &#8617;]]></summary></entry><entry><title type="html">C270 Art</title><link href="https://matthewminer.com/2024/05/25/c270-art" rel="alternate" type="text/html" title="C270 Art" /><published>2024-05-25T00:00:00+00:00</published><updated>2024-05-25T00:00:00+00:00</updated><id>https://matthewminer.com/2024/05/25/c270-art</id><content type="html" xml:base="https://matthewminer.com/2024/05/25/c270-art"><![CDATA[<p>My webcam died. It’s a Logitech C270 HD, a cheap, no-nonsense, blue collar webcam. The working man’s webcam. For years it worked flawlessly, until it didn’t.</p>

<p><a href="https://www.reddit.com/r/LogitechG/comments/u7q9tw/comment/izcg0e2/">This Reddit comment</a> suggests I remove the housing and pull on the camera sensor until the video feed returns to normal. This works, but only for 30 seconds at a time. I admitted defeat. Goodnight sweet prince.</p>

<p><img src="/images/busted-webcam.jpg" alt="Busted webcam" /></p>

<p>On its march to the grave, the glitchy images it produces are striking. Check out these Easter pastels.</p>

<p><img src="/images/webcam-glitch-art-1.jpg" alt="Webcam glitch art" /></p>

<p><img src="/images/webcam-glitch-art-2.jpg" alt="Webcam glitch art" /></p>

<p><img src="/images/webcam-glitch-art-3.jpg" alt="Webcam glitch art" /></p>

<p>Lovely. How about this darker palette.</p>

<p><img src="/images/webcam-glitch-art-4.jpg" alt="Webcam glitch art" /></p>

<p><img src="/images/webcam-glitch-art-5.jpg" alt="Webcam glitch art" /></p>

<p><img src="/images/webcam-glitch-art-6.jpg" alt="Webcam glitch art" /></p>

<p>Its magnum opus is a piece I call <em>Matrix Sunrise</em>.</p>

<p><img src="/images/webcam-glitch-art-7.jpg" alt="Webcam glitch art" /></p>

<p>“Not everyone can become a great artist, but a great artist can come from anywhere.”</p>]]></content><author><name>Matthew Miner</name></author><summary type="html"><![CDATA[My webcam died. It’s a Logitech C270 HD, a cheap, no-nonsense, blue collar webcam. The working man’s webcam. For years it worked flawlessly, until it didn’t.]]></summary></entry></feed>