<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vav Labs</title>
    <description>The latest articles on DEV Community by Vav Labs (@vav_labs).</description>
    <link>https://dev.to/vav_labs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3986932%2F34593eb7-f7c5-49ff-9381-e86c74adb4e9.jpg</url>
      <title>DEV Community: Vav Labs</title>
      <link>https://dev.to/vav_labs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vav_labs"/>
    <language>en</language>
    <item>
      <title>Tower Defense Path Validation in Godot</title>
      <dc:creator>Vav Labs</dc:creator>
      <pubDate>Wed, 24 Jun 2026 15:15:50 +0000</pubDate>
      <link>https://dev.to/vav_labs/tower-defense-path-validation-in-godot-12e2</link>
      <guid>https://dev.to/vav_labs/tower-defense-path-validation-in-godot-12e2</guid>
      <description>&lt;p&gt;In a tower defense, a player can drop one tower that walls off the path and softlocks the wave. The fix isn't to place it and hope pathfinding recovers — it's to validate first: treat the tower as a temporary blocker, ask if a route still exists, revert if it doesn't, and only then commit.&lt;/p&gt;

&lt;p&gt;I built a playable Godot 4.7 demo for it: place towers, watch the path reroute, try to seal it and get rejected. The guide also measures the cheap vs expensive way to run that check, with a runnable project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vav-labs.com/blog/tower-defense-path-validation-in-godot/" rel="noopener noreferrer"&gt;https://vav-labs.com/blog/tower-defense-path-validation-in-godot/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1llhjn6ghfoshlxo7xii.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1llhjn6ghfoshlxo7xii.gif" alt=" " width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>godot</category>
      <category>gamedev</category>
      <category>pathfinding</category>
    </item>
    <item>
      <title>Godot's NavigationObstacle2D has two modes (and other NavigationServer2D gotchas)</title>
      <dc:creator>Vav Labs</dc:creator>
      <pubDate>Tue, 23 Jun 2026 09:42:09 +0000</pubDate>
      <link>https://dev.to/vav_labs/godots-navigationobstacle2d-has-two-modes-and-other-navigationserver2d-gotchas-1l6</link>
      <guid>https://dev.to/vav_labs/godots-navigationobstacle2d-has-two-modes-and-other-navigationserver2d-gotchas-1l6</guid>
      <description>&lt;p&gt;Godot's NavigationServer2D is powerful and quietly confusing. Most of the pain isn't the API — it's a handful of distinctions that look similar and behave nothing alike. The ones that eat afternoons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NavigationObstacle2D has two modes, and people conflate them.&lt;/strong&gt; &lt;code&gt;avoidance_enabled&lt;/code&gt; makes your agents steer around an obstacle, but the path query still runs straight through it — so your tower-defense blocker doesn't block the route. &lt;code&gt;affect_navigation_mesh&lt;/code&gt; is the other one: it carves the obstacle into the navmesh at bake time, and &lt;em&gt;then&lt;/em&gt; the path reroutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The empty path.&lt;/strong&gt; A query returns nothing even though the scene looks walkable — usually because the navmesh was never baked. It's the NavigationServer2D version of forgetting &lt;code&gt;AStarGrid2D.update()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map-sync timing.&lt;/strong&gt; NavigationServer changes apply at the end of the physics frame. Query in &lt;code&gt;_ready()&lt;/code&gt; or right after editing nav data and you get a stale or empty result — then blame the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NavigationAgent2D isn't the map.&lt;/strong&gt; The agent is a node-level helper that consumes navigation data; it doesn't make the map valid by itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I wrote the full guide — regions, agents, direct path queries, both obstacle modes, links, and the map-sync trap — checked every claim against the docs and the C++ source, and built an interactive playground for the gotchas. (It's a browser illustration of how the system behaves, not the engine running, and there's no benchmark here — it's a guide, not a measurement.)&lt;/p&gt;

&lt;p&gt;Full guide + playground: &lt;strong&gt;&lt;a href="https://vav-labs.com/blog/navigationserver2d-godot-complete-guide/" rel="noopener noreferrer"&gt;https://vav-labs.com/blog/navigationserver2d-godot-complete-guide/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If any of it's wrong or out of date for your version, tell me — the navigation API has moved across Godot 4.x, and I'd rather fix it than leave you debugging my mistake.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0x6k3z4a31nv2736h8qk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0x6k3z4a31nv2736h8qk.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AStarGrid2D in Godot 4: the gotchas, and an interactive sandbox</title>
      <dc:creator>Vav Labs</dc:creator>
      <pubDate>Sun, 21 Jun 2026 23:20:15 +0000</pubDate>
      <link>https://dev.to/vav_labs/astargrid2d-in-godot-4-the-gotchas-and-an-interactive-sandbox-3dl8</link>
      <guid>https://dev.to/vav_labs/astargrid2d-in-godot-4-the-gotchas-and-an-interactive-sandbox-3dl8</guid>
      <description>&lt;p&gt;Godot's &lt;code&gt;AStarGrid2D&lt;/code&gt; is a ten-minute read and a month of gotchas. The API page is small, you skim it, and then your path comes back empty, your units cut through wall corners, or your weighted swamp gets ignored. None of it is a bug. It's the gap between knowing the method names and knowing which handful of settings actually decide the behavior.&lt;/p&gt;

&lt;p&gt;The five that get almost everyone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You forget &lt;code&gt;update()&lt;/code&gt;.&lt;/strong&gt; Change the region or cell size, skip the rebuild, and you get an empty array with no error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;update()&lt;/code&gt; wipes your point data.&lt;/strong&gt; It clears every solid cell and weight, so you have to set those &lt;em&gt;after&lt;/em&gt; you call it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagonal corner-cutting.&lt;/strong&gt; &lt;code&gt;DIAGONAL_MODE_ALWAYS&lt;/code&gt; lets units squeeze diagonally between two walls. You usually want &lt;code&gt;ONLY_IF_NO_OBSTACLES&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;jumping_enabled&lt;/code&gt; silently ignores weight scale.&lt;/strong&gt; Turn on JPS and your weighted terrain stops mattering. It's in the docs. People still lose an afternoon to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manhattan overestimates with diagonals.&lt;/strong&gt; On an 8-direction grid it counts a diagonal as two steps, so your "shortest" path isn't. Reach for Octile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I wrote the reference I actually wanted (every property and method, the gotcha attached to each, version-honest across Godot 4.0 → 4.7, checked against the official docs and the C++ source), and built an interactive sandbox for the parts you have to &lt;em&gt;see&lt;/em&gt;: drag the goal, paint solid and weighted cells, flip diagonal modes, toggle jumping, and watch the path recompute live. (It's a browser illustration of how AStarGrid2D behaves, not the engine itself running in a tab.)&lt;/p&gt;

&lt;p&gt;Full reference and the sandbox: &lt;strong&gt;&lt;a href="https://vav-labs.com/blog/astargrid2d-complete-reference/" rel="noopener noreferrer"&gt;https://vav-labs.com/blog/astargrid2d-complete-reference/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If any of it's wrong or out of date for your version, tell me. The API moved across Godot 4.x, and I'd rather fix it than leave you debugging my mistake.&lt;/p&gt;

</description>
      <category>godot</category>
      <category>gamedev</category>
      <category>gdscript</category>
      <category>pathfinding</category>
    </item>
    <item>
      <title>Moving 10,000 agents in Godot without the frame spike</title>
      <dc:creator>Vav Labs</dc:creator>
      <pubDate>Tue, 16 Jun 2026 15:08:59 +0000</pubDate>
      <link>https://dev.to/vav_labs/moving-10000-agents-in-godot-without-the-frame-spike-1l9n</link>
      <guid>https://dev.to/vav_labs/moving-10000-agents-in-godot-without-the-frame-spike-1l9n</guid>
      <description>&lt;p&gt;I kept reading that Godot pathfinding falls apart with a lot of agents, so I actually measured it. 500 agents each calling AStarGrid2D.get_id_path() every frame gave me a 670 ms median frame on an 8-year-old desktop — a slideshow. Swap those 500 per-agent queries for one shared field the whole crowd reads, and the same agents drop to ~2 ms, nothing over the 16.6 ms budget. The fix was the shape of the work, not a faster solver — and the same approach holds 10,000 agents at 77 FPS in a browser tab, pure GDScript.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/4I9pNktBq1M"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Full write-up with the measured ladder out to 20,000 agents, a playable demo (drag the goal, flip naive vs scheduled, push the count yourself), and the benchmark JSON for every count: &lt;a href="https://vav-labs.com/blog/moving-10000-agents-in-godot/" rel="noopener noreferrer"&gt;https://vav-labs.com/blog/moving-10000-agents-in-godot/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>godot</category>
      <category>gamedev</category>
      <category>performance</category>
      <category>gdscript</category>
    </item>
  </channel>
</rss>
