{"id":15,"date":"2026-01-08T21:57:13","date_gmt":"2026-01-08T21:57:13","guid":{"rendered":"https:\/\/jsteinhauer.com\/blog\/?p=15"},"modified":"2026-01-08T21:57:13","modified_gmt":"2026-01-08T21:57:13","slug":"ardenfalls-unity-build-tooling","status":"publish","type":"post","link":"https:\/\/jsteinhauer.com\/blog\/index.php\/2026\/01\/08\/ardenfalls-unity-build-tooling\/","title":{"rendered":"Ardenfall&#8217;s Unity Build Tooling"},"content":{"rendered":"\n<p>Being the only one making builds on the project, there has been little need for a build server. Someday I&#8217;ll set one up, but for now I just run all builds locally. That doesn&#8217;t mean I don&#8217;t have a few tools, and I figured they were interesting to post as my first post on this little blog.<\/p>\n\n\n\n<p><strong>Cell Batcher<\/strong><\/p>\n\n\n\n<p>My world is split into maps (interior and overworld) and my maps are split into cells. A lot of local (not source control) data is baked for both development and build uses. Navmesh and occlusion data, cell previews (used for the ingame map and an editor scene loader), distant cells (simplified prefabs of each cell), and so on.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"388\" height=\"284\" src=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4424.png\" alt=\"\" class=\"wp-image-16\" srcset=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4424.png 388w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4424-300x220.png 300w\" sizes=\"auto, (max-width: 388px) 100vw, 388px\" \/><\/figure>\n\n\n\n<p>I can target all cells, cells loaded in the scene editor, currently selected cells in the cell selector window, or bookmarks (predefined lists of cells).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"681\" src=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4427.png\" alt=\"\" class=\"wp-image-19\" srcset=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4427.png 920w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4427-300x222.png 300w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4427-768x568.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p><em>The World Panel Tool is used to load cells in the editor, but can be used to select certain cells to batch \/ build in a pinch.<\/em><\/p>\n\n\n\n<p>Currently this is a VERY rudimentary part of the build pipeline &#8211; relying on either baking everything (takes ages) for every build, or just manually remembering what data needs to be updated. <\/p>\n\n\n\n<p>I plan on heavily improving this in the future. Storing separate data for different build profiles (IE demo vs release vs development), and tracking when data needs to be automatically updated (ie detecting a scene has been altered and invalidating certain data) are key additions I&#8217;d like to add eventually.<\/p>\n\n\n\n<p><strong>Build Profiles<\/strong><\/p>\n\n\n\n<p>Each build profile has a configuration (steam appid, analytics info, etc), as well as allows for asset stripping certain directories. I can also define what cells are packed by default.<\/p>\n\n\n\n<p><strong>Builder<\/strong><\/p>\n\n\n\n<p>The actual build tool gives a handful of helpful options. <\/p>\n\n\n\n<p>The most interesting is the Pack Cells feature. I can pack all scenes, certain bookmarks, selected cells, and so on. This makes it very easy to only build areas I care about &#8211; for the demo I just pack certain cells, and for a development build I may just pack a single cell I want to test!<\/p>\n\n\n\n<p>There&#8217;s also various development flags and other standard build flags. <\/p>\n\n\n\n<p>Build versions are by default automatically generated, but I can force a certain build version if I desire.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"428\" height=\"417\" src=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4425.png\" alt=\"\" class=\"wp-image-17\" srcset=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4425.png 428w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4425-300x292.png 300w\" sizes=\"auto, (max-width: 428px) 100vw, 428px\" \/><\/figure>\n\n\n\n<p><strong>Steam Uploader<\/strong><\/p>\n\n\n\n<p>I&#8217;ve also made a little tool to automate steam uploads. It simply hooks into Steam&#8217;s<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"492\" height=\"167\" src=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4426.png\" alt=\"\" class=\"wp-image-18\" srcset=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4426.png 492w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4426-300x102.png 300w\" sizes=\"auto, (max-width: 492px) 100vw, 492px\" \/><\/figure>\n\n\n\n<p><strong>Source Control Tooling<\/strong><\/p>\n\n\n\n<p>While not quite related to build tooling, the Source Control tooling is tangentially related. It is also a fun example of building something fancy that ends up being unnecessary.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"977\" height=\"513\" src=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4428.png\" alt=\"\" class=\"wp-image-20\" srcset=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4428.png 977w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4428-300x158.png 300w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4428-768x403.png 768w\" sizes=\"auto, (max-width: 977px) 100vw, 977px\" \/><\/figure>\n\n\n\n<p>A few years ago, I built this very fancy cell git tool, that detects cell data being modified via git, and allowing for certain cells to be easily discarded, staged, unstaged, and so on.<\/p>\n\n\n\n<p>After a few months of seeing the level designers rarely push their work (even with my nagging), I considered an alternative: <em>a single button to stage everything currently loaded.<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"427\" height=\"256\" src=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4429.png\" alt=\"\" class=\"wp-image-21\" srcset=\"https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4429.png 427w, https:\/\/jsteinhauer.com\/blog\/wp-content\/uploads\/2026\/01\/Screenshot_4429-300x180.png 300w\" sizes=\"auto, (max-width: 427px) 100vw, 427px\" \/><\/figure>\n\n\n\n<p>Think of the workflow the devs are doing &#8211; they are loading some cells, and doing work. Thus, a button that just stages the loaded cells is really all they need to do! Naturally they need to also push other assets they may have altered &#8211; prefabs, materials and so on. But my tool was never meant to resolve that anyways! Lesson learned.<\/p>\n\n\n\n<p>I will still use the full tool every once in a while, but not enough to make it worth it. It&#8217;s usually much faster to just stage selected or loaded cells, then discard anything in staging.<\/p>\n\n\n\n<p><strong>End<\/strong><\/p>\n\n\n\n<p>Well, that&#8217;s it for build tooling. Nothing groundbreaking, but still some fun features that makes my life easier. The steam tool in particular has been a huge help! I hope something here gives you an idea for your own project.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Being the only one making builds on the project, there has been little need for a build server. Someday I&#8217;ll set one up, but for now I just run all builds locally. That doesn&#8217;t mean I don&#8217;t have a few tools, and I figured they were interesting to post as my first post on this [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-15","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/15","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=15"}],"version-history":[{"count":1,"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/15\/revisions"}],"predecessor-version":[{"id":22,"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/15\/revisions\/22"}],"wp:attachment":[{"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jsteinhauer.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}