<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://rimworldwiki.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=CryptikLemur</id>
	<title>RimWorld Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://rimworldwiki.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=CryptikLemur"/>
	<link rel="alternate" type="text/html" href="https://rimworldwiki.com/wiki/Special:Contributions/CryptikLemur"/>
	<updated>2026-08-30T08:17:30Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.8</generator>
	<entry>
		<id>https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181292</id>
		<title>Modding Tutorials/Asset Bundles</title>
		<link rel="alternate" type="text/html" href="https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181292"/>
		<updated>2026-07-01T15:27:55Z</updated>

		<summary type="html">&lt;p&gt;CryptikLemur: Add more unity instructions, shader callouts, and making it a little less formal&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Asset Bundles}}&lt;br /&gt;
&lt;br /&gt;
{{BackToTutorials}}&lt;br /&gt;
&lt;br /&gt;
{{:Modding_Tutorials/Under_Review}}&lt;br /&gt;
&lt;br /&gt;
This tutorial explains what asset bundles are, why you might want them, and how they compare to the two other ways RimWorld can load custom art: loose textures and DDS files. It starts from zero, so you don't need to know anything about Unity going in. Later sections walk through the actual build process, then get into the parts that trip people up, mostly shaders and cross-platform issues.&lt;br /&gt;
&lt;br /&gt;
== What is an asset bundle? ==&lt;br /&gt;
&lt;br /&gt;
An asset bundle is a single file that holds a bunch of Unity assets already packed in the format the engine wants. RimWorld runs on Unity, and Unity has its own internal way of storing textures, meshes, shaders, audio, and so on. A loose PNG sitting in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder isn't in that format yet, so the game has to convert it before it can use it.&lt;br /&gt;
&lt;br /&gt;
A bundle is that conversion done ahead of time, saved to disk, and handed to the engine in one piece. The rest of this page is mostly consequences of that.&lt;br /&gt;
&lt;br /&gt;
== Why would I care? ==&lt;br /&gt;
&lt;br /&gt;
Two reasons, mostly:&lt;br /&gt;
&lt;br /&gt;
'''Load time.''' When you ship loose PNGs, RimWorld converts every single one while the game boots (more on this in the next section). For a small mod you'll never notice. For a mod with hundreds or thousands of textures, that conversion adds up, and it's part of why a heavily-modded load screen sits there for a while. A bundle skips the conversion because the work is already done.&lt;br /&gt;
&lt;br /&gt;
'''Everything that isn't a texture.''' Loose files only really work for textures and sounds. If you want to ship a custom shader, a custom font, a custom mesh, or anything else Unity-shaped, a bundle is the normal way to get it into the game. There's no &amp;lt;code&amp;gt;Shaders/&amp;lt;/code&amp;gt; folder you can just drop a file into.&lt;br /&gt;
&lt;br /&gt;
If your mod is XML and a handful of textures, you probably don't need bundles at all. If your mod is large, or it needs a custom shader or font, you do.&lt;br /&gt;
&lt;br /&gt;
As of 1.6, bundles are the encouraged path for anything beyond a small loose-texture mod. The engine grew real first-class support for them: it loads them for you, swaps the right one per operating system, and serves their contents through the same content lookup you already use. Earlier versions made you wire all of that up by hand, which is why older tutorials look more involved than this one. On 1.6+ most of the friction is gone.&lt;br /&gt;
&lt;br /&gt;
== Loose textures vs DDS vs asset bundles ==&lt;br /&gt;
&lt;br /&gt;
All three of these get a texture onto the screen. They differ in '''when''' the work happens and '''what''' they can carry. Mix those two questions up and the whole topic gets confusing fast.&lt;br /&gt;
&lt;br /&gt;
=== Loose textures (PNG) ===&lt;br /&gt;
&lt;br /&gt;
You put a &amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt; in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder and reference it by path. Simplest possible setup, and what almost every tutorial starts with. (The [[Modding_Tutorials/Textures|Textures]] page covers the path rules and texture conventions in full; this page assumes you have that down.)&lt;br /&gt;
&lt;br /&gt;
RimWorld doesn't render your PNG directly. On load it converts the PNG into a compressed GPU texture (the same family of formats DDS uses) and caches the result. So the PNG is a source file, not the thing the GPU actually draws. That conversion is cheap for one texture and not cheap for a thousand.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' dead simple, easy to edit, easy to diff, no tooling.&lt;br /&gt;
* '''Con:''' the game pays a conversion cost on load, and you can't ship anything other than textures and sounds this way.&lt;br /&gt;
&lt;br /&gt;
=== DDS textures ===&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;code&amp;gt;.dds&amp;lt;/code&amp;gt; file is a texture that's ''already'' in a GPU-ready compressed format. If you drop a DDS next to where the PNG would go, RimWorld uses it directly and skips the conversion step entirely.&lt;br /&gt;
&lt;br /&gt;
So the loose-vs-DDS question is mostly about that conversion cost, not about how the final image looks. A correctly-made DDS and the game's own PNG conversion land in roughly the same place visually. What DDS buys you is a faster load and no first-load hitch, because the expensive part already happened on your machine when you made the file.&lt;br /&gt;
&lt;br /&gt;
The catch is you have to ''make'' the DDS, and if you make it wrong (wrong compression, no mipmaps, wrong color space) it can look worse than just letting the game convert the PNG. It's a real win for large texture sets, and a footgun for people who don't know what BC7 or mipmaps are.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' no runtime conversion, faster load, no first-load stutter for that texture.&lt;br /&gt;
* '''Con:''' you have to generate it correctly, harder to edit after the fact, much larger on disk than the source PNGs, and still textures-only.&lt;br /&gt;
&lt;br /&gt;
=== Asset bundles ===&lt;br /&gt;
&lt;br /&gt;
A bundle is the next step up. It's one file that can hold many textures (already in GPU format, like DDS, so same load-time win) ''plus'' meshes, shaders, fonts, and audio. Instead of hundreds of loose files converted one by one, the engine loads one bundle and everything inside is ready to go.&lt;br /&gt;
&lt;br /&gt;
The trade is that a bundle is opaque. You can't open it in an image editor and tweak a pixel. You rebuild it from your source assets, which means you keep your editable PNGs and Unity sources somewhere and treat the bundle as a build output, the same way you treat a compiled DLL.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' fastest load for large asset sets, one file instead of thousands, and it's the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
* '''Con:''' you need a build step and the Unity tooling to produce it, and the output isn't hand-editable.&lt;br /&gt;
&lt;br /&gt;
=== Quick comparison ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Approach !! Load cost !! Disk size !! Can carry !! Editable after build? !! Good for&lt;br /&gt;
|-&lt;br /&gt;
| Loose PNG || Converted every load || Small || Textures, sounds || Yes || Small mods, prototyping&lt;br /&gt;
|-&lt;br /&gt;
| DDS || None (pre-converted) || Large || Textures || Painful || Large texture sets&lt;br /&gt;
|-&lt;br /&gt;
| Asset bundle || None (pre-converted) || Smallest || Textures, meshes, shaders, fonts, audio || No (rebuild from source) || Large mods, anything non-texture&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
That disk-size column is worth some real numbers. Take one texture set of around 960 files and store it all three ways:&lt;br /&gt;
&lt;br /&gt;
* As loose PNGs: about 40 MB.&lt;br /&gt;
* Converted to DDS: about 230 MB. The conversion skips the runtime work but blows up the size on disk.&lt;br /&gt;
* Packed into a single asset bundle: about 28 MB.&lt;br /&gt;
&lt;br /&gt;
So DDS is the ''largest'' on disk, not the smallest. It trades space for skipping the load-time conversion. The bundle gets that same load-time win and still ends up the smallest of the three, because it's compressed into one file. (Numbers from a comparison shared in the [https://discord.com/channels/214523379766525963/632790371256238120/1405733585268375643 RimWorld modding Discord]; your exact figures will vary with the texture set and compression settings.)&lt;br /&gt;
&lt;br /&gt;
== The workflow, in plain terms ==&lt;br /&gt;
&lt;br /&gt;
Before any code or folder layout, here's what building a bundle actually looks like from a height:&lt;br /&gt;
&lt;br /&gt;
# You keep your real, editable source assets (PNGs, Unity materials, shader files, font files) in a Unity project.&lt;br /&gt;
# In that Unity project you tag each asset with a bundle name, then tell Unity to build the bundles.&lt;br /&gt;
# Unity spits out the bundle files.&lt;br /&gt;
# You ship those bundle files inside your RimWorld mod.&lt;br /&gt;
# At game load, RimWorld finds the bundle on its own and serves its contents through the normal content lookup (1.6 does the loading for you; textures and sounds resolve by their existing path, while shaders and fonts you still fetch by name in code).&lt;br /&gt;
&lt;br /&gt;
That's the loop. You edit in Unity, build, copy the output into your mod, and the game picks it up. The first time through it feels like a lot of ceremony for a texture. Once it's set up, adding a new asset is just &amp;quot;drop it in the Unity project, rebuild, copy.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
You don't have to do every step by hand. The next section covers that before the build details, so you can go find a tool first if you'd rather not set this up yourself.&lt;br /&gt;
&lt;br /&gt;
== Before we get started: Do I actually have to do everything below? ==&lt;br /&gt;
&lt;br /&gt;
No. Setting up the Unity project, writing the build script, and juggling per-platform bundles is fiddly enough that several modders have written tools to do it for you. They wrap the whole thing, handle the multi-platform build, and spit out ready-to-ship bundles without you babysitting the Unity editor. If that sounds like what you want, you can stop here and go find one; the rest of this page is for when you want to understand what those tools are doing or build it yourself.&lt;br /&gt;
&lt;br /&gt;
This tutorial doesn't point at any specific one on purpose, since they come and go and get superseded. A search on Google or GitHub for RimWorld asset bundle tooling turns them up, and the modding Discord communities are usually the fastest place to find out which one people are actually using right now and which one is abandoned. Ask there before sinking an afternoon into a tool that got replaced six months ago.&lt;br /&gt;
&lt;br /&gt;
== Branch: the real build steps ==&lt;br /&gt;
&lt;br /&gt;
If you want the actual mechanics, here they are. First, install the right Unity editor. This matters more than anything else on this page: a bundle built on the wrong Unity version may silently fail to load. RimWorld 1.6 runs on '''Unity 2022.3.35f1''', so install exactly that version through the Unity Hub (under &amp;quot;Add&amp;quot; you can pick a specific archived version, or grab it from the Unity download archive). Build your bundles in 2022.3.35f1 and they line up with what the game expects. If RimWorld updates to a new Unity version down the line, you rebuild against the new one.&lt;br /&gt;
&lt;br /&gt;
=== Folder layout ===&lt;br /&gt;
&lt;br /&gt;
Inside your mod, bundles live in an &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder. For most mods that's a single file:&lt;br /&gt;
&lt;br /&gt;
 MyMod/&lt;br /&gt;
   About/&lt;br /&gt;
   Defs/&lt;br /&gt;
   Textures/&lt;br /&gt;
   AssetBundles/&lt;br /&gt;
     mymod_assets&lt;br /&gt;
&lt;br /&gt;
The bundle file itself has no extension. You name it whatever you tagged it in Unity. There's no &amp;lt;code&amp;gt;.dll&amp;lt;/code&amp;gt; required to use a bundle: a pure XML-and-textures mod can ship a bundle and never write a line of C#, because the game loads it and serves its contents automatically.&lt;br /&gt;
&lt;br /&gt;
'''You only need per-OS copies if the bundle contains any shaders.''' Textures, meshes, and fonts are identical across platforms, so one bundle with no suffix loads everywhere. Audio is usually fine in a no-suffix bundle too, but its import settings can be overridden per platform, so if a bundled sound misbehaves on one OS, that's a place to look. A compiled shader is the real platform-specific case, so a bundle with shaders in it has to be built once per operating system and named with an OS suffix (&amp;lt;code&amp;gt;_win&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;_mac&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;_linux&amp;lt;/code&amp;gt;) so the game loads the right one. The details section covers why. If you aren't shipping any shaders, ignore the suffix entirely and ship the one file.&lt;br /&gt;
&lt;br /&gt;
=== Setting up the Unity project ===&lt;br /&gt;
&lt;br /&gt;
If you have never opened Unity, here's the whole thing from scratch. You only do this setup once; after that, adding assets is just dropping files in and rebuilding.&lt;br /&gt;
&lt;br /&gt;
# '''Open Unity Hub and create a new project.''' Hit &amp;quot;New project&amp;quot;, pick the '''2022.3.35f1''' editor you installed earlier, and choose the ''2D (Built-In Render Pipeline)'' template. RimWorld's art is flat 2D, so the 2D template gives you sane defaults. Name it something like &amp;lt;code&amp;gt;MyModAssets&amp;lt;/code&amp;gt; and create it. This is a separate project from your mod folder; it's just your asset workshop.&lt;br /&gt;
# '''Find the Assets folder.''' Once the editor opens, the Project window (bottom of the screen by default) shows a folder called &amp;lt;code&amp;gt;Assets&amp;lt;/code&amp;gt;. Everything you want in a bundle goes somewhere under here. On disk this is &amp;lt;code&amp;gt;MyModAssets/Assets/&amp;lt;/code&amp;gt; in the project folder you just made.&lt;br /&gt;
# '''Copy your source art in.''' Drag your real, editable PNGs into the Project window, or copy them into &amp;lt;code&amp;gt;MyModAssets/Assets/&amp;lt;/code&amp;gt; in your file browser and let Unity import them. Mirror the same folder structure your mod uses, so a texture that lives at &amp;lt;code&amp;gt;Textures/Things/Item/MyThing&amp;lt;/code&amp;gt; in your mod goes to &amp;lt;code&amp;gt;Assets/Textures/Things/Item/MyThing.png&amp;lt;/code&amp;gt; here. That path matters later: the game looks the asset up by the path it has ''inside'' the bundle, so it has to match where the loose file would have lived.&lt;br /&gt;
&lt;br /&gt;
=== Tagging assets and adding the build script ===&lt;br /&gt;
&lt;br /&gt;
# '''Tag each asset with a bundle name.''' Click an asset in the Project window. At the very bottom of the Inspector (right side of the screen) there's an '''AssetBundle''' dropdown, set to &amp;quot;None&amp;quot; by default. Click it, pick &amp;quot;New...&amp;quot;, and type a bundle name like &amp;lt;code&amp;gt;mymod_assets&amp;lt;/code&amp;gt;. Every asset you give that same name builds into one bundle together. You can multi-select a whole folder of textures and tag them all at once.&lt;br /&gt;
# '''Make an Editor folder for the build script.''' In the Project window, right-click &amp;lt;code&amp;gt;Assets&amp;lt;/code&amp;gt;, choose Create &amp;gt; Folder, and name it exactly &amp;lt;code&amp;gt;Editor&amp;lt;/code&amp;gt;. The name isn't optional: Unity only runs editor scripts (the ones that add menu items) from a folder literally named &amp;lt;code&amp;gt;Editor&amp;lt;/code&amp;gt;. Put the script anywhere else and the menu item never shows up.&lt;br /&gt;
# '''Create the script.''' Right-click your new &amp;lt;code&amp;gt;Editor&amp;lt;/code&amp;gt; folder, choose Create &amp;gt; C# Script, name it &amp;lt;code&amp;gt;BundleBuilder&amp;lt;/code&amp;gt;, then double-click it and replace the contents with the code below. The filename has to match the class name, so &amp;lt;code&amp;gt;BundleBuilder.cs&amp;lt;/code&amp;gt; holds &amp;lt;code&amp;gt;class BundleBuilder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Unity doesn't expose bundle building in the default menus, which is why you need this script at all. The bare version is just one &amp;lt;code&amp;gt;BuildPipeline.BuildAssetBundles&amp;lt;/code&amp;gt; call. It works, but it ships your textures with whatever import settings they happened to have, which is usually uncompressed and wasteful. Controlling how each texture imports before it gets packed is what actually shrinks the disk and memory footprint, so the script below does two things: it walks your textures and sets sane import settings on each, then builds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using UnityEditor;&lt;br /&gt;
using UnityEngine;&lt;br /&gt;
using System.IO;&lt;br /&gt;
&lt;br /&gt;
public static class BundleBuilder&lt;br /&gt;
{&lt;br /&gt;
    [MenuItem(&amp;quot;Assets/Build AssetBundles&amp;quot;)]&lt;br /&gt;
    static void Build()&lt;br /&gt;
    {&lt;br /&gt;
        // First pass: set good import settings on every texture in the project.&lt;br /&gt;
        foreach (string guid in AssetDatabase.FindAssets(&amp;quot;t:Texture2D&amp;quot;))&lt;br /&gt;
        {&lt;br /&gt;
            string assetPath = AssetDatabase.GUIDToAssetPath(guid);&lt;br /&gt;
            if (AssetImporter.GetAtPath(assetPath) is not TextureImporter tex) continue;&lt;br /&gt;
&lt;br /&gt;
            string lower = assetPath.ToLower();&lt;br /&gt;
            bool isTerrain = lower.Contains(&amp;quot;/terrain/&amp;quot;);&lt;br /&gt;
            bool isMask    = lower.Contains(&amp;quot;_mask&amp;quot;);&lt;br /&gt;
            bool isNormal  = lower.Contains(&amp;quot;_normal&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            // GUI type suits RimWorld's flat 2D art and avoids Unity generating sprite sub-assets.&lt;br /&gt;
            tex.textureType       = isNormal ? TextureImporterType.NormalMap : TextureImporterType.GUI;&lt;br /&gt;
            tex.alphaIsTransparency = true;&lt;br /&gt;
&lt;br /&gt;
            // Mask and normal maps hold raw data, not color, so they must stay linear (sRGB off).&lt;br /&gt;
            tex.sRGBTexture = !isMask &amp;amp;&amp;amp; !isNormal;&lt;br /&gt;
&lt;br /&gt;
            // Terrain tiles repeat across the ground; everything else should clamp at its edges.&lt;br /&gt;
            tex.wrapMode  = isTerrain ? TextureWrapMode.Repeat : TextureWrapMode.Clamp;&lt;br /&gt;
            tex.anisoLevel = isTerrain ? 8 : 1;&lt;br /&gt;
&lt;br /&gt;
            tex.filterMode    = FilterMode.Trilinear;&lt;br /&gt;
            tex.mipmapEnabled = true;&lt;br /&gt;
&lt;br /&gt;
            // BC7 high-quality compression: small on disk and in VRAM, good for hand-painted art.&lt;br /&gt;
            tex.SetPlatformTextureSettings(new TextureImporterPlatformSettings&lt;br /&gt;
            {&lt;br /&gt;
                name       = &amp;quot;Standalone&amp;quot;,&lt;br /&gt;
                overridden = true,&lt;br /&gt;
                format     = TextureImporterFormat.BC7,&lt;br /&gt;
                maxTextureSize = 4096,&lt;br /&gt;
                textureCompression = TextureImporterCompression.CompressedHQ&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            tex.SaveAndReimport();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Second pass: build the bundles.&lt;br /&gt;
        string output = &amp;quot;Assets/BuiltBundles&amp;quot;;&lt;br /&gt;
        Directory.CreateDirectory(output);&lt;br /&gt;
        BuildPipeline.BuildAssetBundles(&lt;br /&gt;
            output,&lt;br /&gt;
            BuildAssetBundleOptions.ChunkBasedCompression, // LZ4&lt;br /&gt;
            BuildTarget.StandaloneWindows64&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A few notes on what that import pass is doing:&lt;br /&gt;
&lt;br /&gt;
* '''BC7 + CompressedHQ''' is the compression that gets you the small disk and VRAM footprint. Without it the texture sits in memory uncompressed.&lt;br /&gt;
* '''sRGB off for masks and normal maps.''' A stuff-color mask or a normal map stores data, not a picture, so it must be read as linear. Leaving sRGB on silently corrupts it. The script keys off &amp;lt;code&amp;gt;_mask&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;_normal&amp;lt;/code&amp;gt; in the filename, so name your files accordingly.&lt;br /&gt;
* '''Terrain repeats, everything else clamps.''' Terrain tiles need &amp;lt;code&amp;gt;Repeat&amp;lt;/code&amp;gt; wrap so they tile seamlessly across the ground; a regular sprite wants &amp;lt;code&amp;gt;Clamp&amp;lt;/code&amp;gt; so its edges don't bleed. The script keys off a &amp;lt;code&amp;gt;/Terrain/&amp;lt;/code&amp;gt; folder in the path.&lt;br /&gt;
* '''Mipmaps on''' so the texture doesn't shimmer when the camera zooms out.&lt;br /&gt;
&lt;br /&gt;
This is a trimmed-down version of what dedicated bundle-building tools do; they also handle audio compression, font atlas generation, and per-bundle include/exclude rules. The texture settings above are the ones you actually want to set by hand.&lt;br /&gt;
&lt;br /&gt;
=== Running the build and collecting the output ===&lt;br /&gt;
&lt;br /&gt;
# '''Let Unity compile the script.''' After you paste the code and save the file, switch back to the Unity editor. It recompiles editor scripts automatically and you'll see a brief spinner in the bottom-right. If there's a compile error it shows up as a red message in the Console window (Window &amp;gt; General &amp;gt; Console); fix it before going further, because a script that doesn't compile won't register its menu item.&lt;br /&gt;
# '''Run it.''' Once it compiles, a new '''Assets''' menu entry appears at the top of the editor: '''Assets &amp;gt; Build AssetBundles''' (that's the &amp;lt;code&amp;gt;[MenuItem(&amp;quot;Assets/Build AssetBundles&amp;quot;)]&amp;lt;/code&amp;gt; line in the script). Click it. Unity reimports every texture with the settings above, then writes the bundles. This can take a while on a big set; watch the progress bar.&lt;br /&gt;
# '''Find the output.''' The script writes to &amp;lt;code&amp;gt;Assets/BuiltBundles/&amp;lt;/code&amp;gt; inside the project. Each bundle you tagged shows up there as a file with no extension (for example &amp;lt;code&amp;gt;mymod_assets&amp;lt;/code&amp;gt;), alongside a &amp;lt;code&amp;gt;.manifest&amp;lt;/code&amp;gt; file and one folder-level manifest. You only ship the extensionless bundle files; the &amp;lt;code&amp;gt;.manifest&amp;lt;/code&amp;gt; files are just Unity's bookkeeping and RimWorld ignores them.&lt;br /&gt;
# '''Copy the bundle into your mod.''' Copy the extensionless file into your mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder. For a bundle with no shaders in it, that's the whole job: no suffix, one file, and it loads on every platform. So &amp;lt;code&amp;gt;mymod_assets&amp;lt;/code&amp;gt; just moves straight into &amp;lt;code&amp;gt;MyMod/AssetBundles/&amp;lt;/code&amp;gt; and you are done. You only add an OS suffix and rebuild per platform if the bundle contains any shaders, which the details section covers.&lt;br /&gt;
&lt;br /&gt;
That's the full loop the first time. From here on, adding an asset is: drop it in the Unity project, tag it, run '''Assets &amp;gt; Build AssetBundles''' again, copy the rebuilt file over.&lt;br /&gt;
&lt;br /&gt;
=== Loading the bundle in your mod ===&lt;br /&gt;
&lt;br /&gt;
In 1.6 you don't load the bundle yourself at all, which catches people off guard since the older docs make it look involved. RimWorld scans every active mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder on startup, loads what it finds, and keeps the loaded bundles on the mod. You don't write &amp;lt;code&amp;gt;AssetBundle.LoadFromFile&amp;lt;/code&amp;gt;, you don't call &amp;lt;code&amp;gt;LoadAsset&amp;lt;/code&amp;gt;, you don't run anything in a static constructor.&lt;br /&gt;
&lt;br /&gt;
The game's normal content lookup also checks bundles for you. When you ask for a texture the usual way, with &amp;lt;code&amp;gt;ContentFinder&amp;lt;Texture2D&amp;gt;.Get(&amp;quot;Things/Item/MyThing&amp;quot;)&amp;lt;/code&amp;gt;, RimWorld walks three places in order: first it looks for a loose file at that path in any active mod, then it falls back to the base game's built-in resources, and only if both miss does it look inside your loaded bundles at the matching path. Same call, same path string, whether the texture is loose or bundled. The asset just has to live at the same path inside the bundle that the loose file would have used (so an asset packed at &amp;lt;code&amp;gt;Textures/Things/Item/MyThing&amp;lt;/code&amp;gt; resolves for the path above).&lt;br /&gt;
&lt;br /&gt;
The practical upshot: for textures and sounds you don't change a single line of your mod's code to switch from loose to bundled. You reference them by path like you always did. You only build a bundle, drop it in the folder, and the game finds the contents.&lt;br /&gt;
&lt;br /&gt;
'''A bundle can't overwrite or retexture a base game texture.''' Because bundles are checked last, after the base game's own resources, the vanilla texture is always found first and your bundled version at the same path is never reached. Bundles are for adding new content, not replacing what ships with the game. If you want to retexture vanilla art, you still do it the old way: a loose file at the matching path (which is checked before the base resources), or a def or &amp;lt;code&amp;gt;texPath&amp;lt;/code&amp;gt; redirect that points the vanilla thing at your own texture. The same goes for sounds.&lt;br /&gt;
&lt;br /&gt;
The exception is shaders. The content lookup skips the loose-file step for shaders entirely (there's no loose-shader path), so a shader is only ever found inside a bundle. &amp;lt;code&amp;gt;ContentFinder&amp;lt;Shader&amp;gt;.Get(&amp;quot;MyShaderName&amp;quot;)&amp;lt;/code&amp;gt; works, but only because the shader is in a bundle for it to find.&lt;br /&gt;
&lt;br /&gt;
== Branch: the details ==&lt;br /&gt;
&lt;br /&gt;
Most of this only comes up once you are shipping a shader or a font. A small texture-only mod can skip the whole section.&lt;br /&gt;
&lt;br /&gt;
=== Shaders and the cross-platform problem ===&lt;br /&gt;
&lt;br /&gt;
A compiled shader is platform-specific, which is why a lot of mods end up shipping multiple bundles. A shader built for DirectX (Windows) isn't the same bytes as one built for Metal (Mac) or Vulkan/OpenGL (Linux). If you build one bundle on Windows and ship it, your shader works on Windows and quietly fails on Mac and Linux, usually showing up as bright magenta where the texture should be.&lt;br /&gt;
&lt;br /&gt;
The fix is to build the shader bundle once per platform, and 1.6 makes this almost free because the game does the picking for you. Name the files with the suffix the game looks for:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_shaders_win&amp;lt;/code&amp;gt; loads on Windows&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_shaders_mac&amp;lt;/code&amp;gt; loads on Mac&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_shaders_linux&amp;lt;/code&amp;gt; loads on Linux&lt;br /&gt;
&lt;br /&gt;
The game checks the running platform and loads only the matching one. A bundle with no suffix at all loads on every platform, which is fine for textures, meshes, sounds, and fonts that don't care, but not safe for shaders.&lt;br /&gt;
&lt;br /&gt;
So for a bundle that has shaders in it, you build the same assets three times in Unity with different &amp;lt;code&amp;gt;BuildTarget&amp;lt;/code&amp;gt; values (&amp;lt;code&amp;gt;StandaloneWindows64&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneOSX&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneLinux64&amp;lt;/code&amp;gt;), give each the right suffix, and ship all three. You write no platform-detection code yourself.&lt;br /&gt;
&lt;br /&gt;
Since only shaders need the triple build, put them in their own bundle. Tag your shaders with one bundle name and everything else with another. Then you build the shader bundle three times (one per OS, suffixed) and the everything-else bundle just once (no suffix). Your folder ends up like this:&lt;br /&gt;
&lt;br /&gt;
 AssetBundles/&lt;br /&gt;
   mymod_assets            (textures, meshes, sounds, fonts, no suffix, loads everywhere)&lt;br /&gt;
   mymod_shaders_win&lt;br /&gt;
   mymod_shaders_mac&lt;br /&gt;
   mymod_shaders_linux&lt;br /&gt;
&lt;br /&gt;
That way your big texture set gets packed once instead of three times, and only the tiny shader bundle pays the per-platform cost.&lt;br /&gt;
&lt;br /&gt;
When a mod works for everyone except the Mac and Linux players, who see pink everywhere, this is almost always the cause.&lt;br /&gt;
&lt;br /&gt;
=== Sounds ===&lt;br /&gt;
&lt;br /&gt;
Audio can go in a bundle as an &amp;lt;code&amp;gt;AudioClip&amp;lt;/code&amp;gt;, and for a lot of sounds it's fine and saves you the loose-file management. RimWorld can also load loose audio through its normal sound def system, so for ordinary sound effects you often don't need a bundle at all. Where bundles help is when you want the audio packed with everything else, or when you are loading clips directly from code rather than through a SoundDef.&lt;br /&gt;
&lt;br /&gt;
=== Fonts ===&lt;br /&gt;
&lt;br /&gt;
If you want a custom font in your UI, a bundle is the way. You import the font into Unity, build it into a bundle, load it as a &amp;lt;code&amp;gt;Font&amp;lt;/code&amp;gt;, and assign it where you draw text. There's no loose-font path in RimWorld at all, so here the bundle isn't a speed-up, it's the only way in.&lt;br /&gt;
&lt;br /&gt;
=== Meshes ===&lt;br /&gt;
&lt;br /&gt;
Same story as fonts. Custom 3D meshes (not the usual flat sprites, actual geometry) come in through a bundle as a &amp;lt;code&amp;gt;Mesh&amp;lt;/code&amp;gt;. Most RimWorld mods never touch this, but if you are doing something with real geometry it's here.&lt;br /&gt;
&lt;br /&gt;
=== Compression and a couple of gotchas ===&lt;br /&gt;
&lt;br /&gt;
* '''Unity version mismatch.''' A bundle built on a different Unity version than the game uses may refuse to load, often with no clear error, the asset just comes back null. Build on Unity 2022.3.35f1 for RimWorld 1.6. This is the single most common reason a bundle that worked in the editor does nothing in-game.&lt;br /&gt;
* '''Bundle file compression.''' This is how the bundle file is packed on disk, separate from texture compression below. Bundles can be built uncompressed, LZ4, or LZMA. LZ4 is the usual sweet spot: small enough, and it doesn't stall on load the way LZMA can. &amp;lt;code&amp;gt;BuildAssetBundleOptions.ChunkBasedCompression&amp;lt;/code&amp;gt; gives you LZ4.&lt;br /&gt;
* '''In-memory texture compression and the white-outline bug.''' Loose textures are compressed in memory once loaded, and that compression is what produces the faint white halo people sometimes see around a sprite (the art program threw away color data in fully transparent pixels). A bundle lets you turn that in-memory compression off for its textures, which sidesteps the halo without the usual workaround of padding the outline. The [[Modding_Tutorials/Textures|Textures]] page documents the bug and the loose-texture fix.&lt;br /&gt;
* '''Shader stripping.''' Unity sometimes strips shader variants it thinks are unused during the build, and then your shader looks wrong at runtime because the variant you needed got cut. If a shader misbehaves only in the built bundle but works in the editor, this is a likely suspect.&lt;br /&gt;
* '''Color space.''' If your textures look washed out or too dark coming out of a bundle, check that the import settings and color space match what the game expects. This is the same class of problem that makes a hand-rolled DDS look wrong.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
* Loose PNG: simplest, but converted on every load and limited to textures and sounds.&lt;br /&gt;
* DDS: a texture pre-converted to GPU format, so it skips the load-time conversion. Same final look, faster load, but you have to make it right, and it lands much larger on disk than the source PNGs.&lt;br /&gt;
* Asset bundle: one pre-packed file that carries textures, meshes, shaders, fonts, and audio. The fastest option for big mods and the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
&lt;br /&gt;
If your mod is small, stay loose. If it's large or needs anything Unity-shaped beyond a texture, learn bundles, and on 1.6+ that's the encouraged path anyway since the engine does the loading and per-OS swapping for you. One no-suffix bundle covers textures, meshes, sounds, and fonts on every platform. Only shaders need per-OS copies, so put your shaders in their own bundle, build that one per platform with the right suffix, and ship the rest as a single file, or your Mac and Linux players get magenta.&lt;br /&gt;
&lt;br /&gt;
[[Category:Modding]]&lt;br /&gt;
[[Category:Modding tutorials]]&lt;/div&gt;</summary>
		<author><name>CryptikLemur</name></author>
	</entry>
	<entry>
		<id>https://rimworldwiki.com/index.php?title=Modding_Tutorials/MayRequire&amp;diff=181187</id>
		<title>Modding Tutorials/MayRequire</title>
		<link rel="alternate" type="text/html" href="https://rimworldwiki.com/index.php?title=Modding_Tutorials/MayRequire&amp;diff=181187"/>
		<updated>2026-06-28T15:37:02Z</updated>

		<summary type="html">&lt;p&gt;CryptikLemur: _steam bug has been fixed in 1.6&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:MayRequire}}&lt;br /&gt;
{{BackToTutorials}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is an [[Modding_Tutorials/Introduction_to_XML|XML attribute]] introduced alongside the [[Royalty DLC]] that allows for easy conditional loading of XML content. &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; makes it easier to load content that is optionally dependent on DLCs or other mods.&lt;br /&gt;
&lt;br /&gt;
== Details ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is inserted as an XML attribute in a supported XML node with a value being one or more &amp;lt;code&amp;gt;packageId&amp;lt;/code&amp;gt; values separated by commas.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;packageId&amp;lt;/code&amp;gt; for official DLCs are:&lt;br /&gt;
&lt;br /&gt;
* Royalty: &amp;lt;code&amp;gt;Ludeon.RimWorld.Royalty&amp;lt;/code&amp;gt;&lt;br /&gt;
* Ideology: &amp;lt;code&amp;gt;Ludeon.RimWorld.Ideology&amp;lt;/code&amp;gt;&lt;br /&gt;
* Biotech: &amp;lt;code&amp;gt;Ludeon.RimWorld.Biotech&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anomaly: &amp;lt;code&amp;gt;Ludeon.RimWorld.Anomaly&amp;lt;/code&amp;gt;&lt;br /&gt;
* Odyssey: &amp;lt;code&amp;gt;Ludeon.RimWorld.Odyssey&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;packageId&amp;lt;/code&amp;gt; for mods can be found in their [[About.xml]] file.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;MayRequireAnyOf&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
By default, &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; will only allow the use of that node if ''all'' of the DLCs or mods it designates are loaded. If you instead want to load the specified node if ''any'' of the specified DLCs or mods are loaded, you can use &amp;lt;code&amp;gt;MayRequireAnyOf&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
&lt;br /&gt;
=== Specifying Multiple Targets ===&lt;br /&gt;
&lt;br /&gt;
Both &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;MayRequireAnyOf&amp;lt;/code&amp;gt; can accept a comma-delimited list of &amp;lt;code&amp;gt;packageId&amp;lt;/code&amp;gt; values. For example, if you wanted to create a &amp;lt;code&amp;gt;ThingDef&amp;lt;/code&amp;gt; that is only loaded if both the [[Royalty]] and [[Ideology]] DLCs are loaded, you could use the following syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ThingDef MayRequire=&amp;quot;Ludeon.RimWorld.Royalty,Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Entries ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; can be on any &amp;lt;code&amp;gt;li&amp;lt;/code&amp;gt; node to only load that entry if the specified DLC or mod is loaded:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;TutorialTableWrapper&amp;quot;&amp;gt;&lt;br /&gt;
{| class=&amp;quot;TutorialCodeTable&amp;quot;&lt;br /&gt;
! XML Example !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;SurgeryOutcomeEffectDef Name=&amp;quot;SurgeryOutcomeBase&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;SurgeryOutcomeBase&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;outcomes&amp;gt;&lt;br /&gt;
    &amp;lt;li Class=&amp;quot;SurgeryOutcomeSuccess&amp;quot; /&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;li Class=&amp;quot;SurgeryOutcome_FailureWithHediff&amp;quot;  MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;chance&amp;gt;0.03&amp;lt;/chance&amp;gt;&lt;br /&gt;
      &amp;lt;failedHediff&amp;gt;Sterilized&amp;lt;/failedHediff&amp;gt;&lt;br /&gt;
      &amp;lt;applyToRecipes&amp;gt;&lt;br /&gt;
        &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;ImplantIUD&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;RemoveIUD&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/applyToRecipes&amp;gt;&lt;br /&gt;
      &amp;lt;failure&amp;gt;true&amp;lt;/failure&amp;gt;&lt;br /&gt;
      &amp;lt;totalDamage&amp;gt;10&amp;lt;/totalDamage&amp;gt;&lt;br /&gt;
      &amp;lt;applyEffectsToPart&amp;gt;true&amp;lt;/applyEffectsToPart&amp;gt;&lt;br /&gt;
      &amp;lt;letterLabel&amp;gt;Surgery failed on {PATIENT_labelShort}: Sterilized&amp;lt;/letterLabel&amp;gt;&lt;br /&gt;
      &amp;lt;letterText&amp;gt;{SURGEON_labelShort} has failed while operating on {PATIENT_labelShort} ({RECIPE_label}), leaving {PATIENT_objective} sterile.&amp;lt;/letterText&amp;gt;&lt;br /&gt;
    &amp;lt;/li&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
  &amp;lt;/outcomes&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/SurgeryOutcomeEffectDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used in the list of possible surgery outcomes to add the possibility of accidentally sterilizing the patient as a result of a surgery failure.&lt;br /&gt;
&lt;br /&gt;
Note that the use of &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; on the list nodes of the &amp;lt;code&amp;gt;&amp;lt;applyToRecipes&amp;gt;&amp;lt;/code&amp;gt; is technically unnecessary as the entire outcome node would not have loaded without [[Biotech]] active.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;DesignationCategoryDef&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;Zone&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;zone&amp;lt;/label&amp;gt;&lt;br /&gt;
  &amp;lt;order&amp;gt;800&amp;lt;/order&amp;gt;&lt;br /&gt;
  &amp;lt;specialDesignatorClasses&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_Cancel&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_Deconstruct&amp;lt;/li&amp;gt; &lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_ZoneAddStockpile_Resources&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_ZoneAddStockpile_Dumping&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_ZoneAdd_Growing&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_ZoneDelete&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaHomeExpand&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaHomeClear&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaAllowedExpand&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaAllowedClear&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaBuildRoof&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaNoRoof&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaIgnoreRoof&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaSnowClearExpand&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Designator_AreaSnowClearClear&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;Designator_AreaPollutionClearExpand&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;Designator_AreaPollutionClearClear&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;/specialDesignatorClasses&amp;gt;&lt;br /&gt;
&amp;lt;/DesignationCategoryDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used in the DesignationCategoryDef for the &amp;quot;Zone&amp;quot; architect menu to add designators for pollution clearing areas.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;AlienRace.ThingDef_AlienRace Name=&amp;quot;ARR_RaceBase&amp;quot; ParentName=&amp;quot;HumanRace&amp;quot; Abstract=&amp;quot;True&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;comps&amp;gt;&lt;br /&gt;
    &amp;lt;li Class=&amp;quot;ARimReborn.CompProperties_ClassUser&amp;quot; MayRequire=&amp;quot;Aelanna.ARimReborn.ClassesAndJobs&amp;quot; /&amp;gt;&lt;br /&gt;
  &amp;lt;/comps&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;inspectorTabs&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Aelanna.ARimReborn.ClassesAndJobs&amp;quot;&amp;gt;ARimReborn.ITab_Pawn_Classes&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;/inspectorTabs&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/AlienRace.ThingDef_AlienRace&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; can be used to omit entire code components.&lt;br /&gt;
&lt;br /&gt;
In this modded example, both a ThingComp and an inspector tab are only added if a specific sub-mod is loaded.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Operation Class=&amp;quot;PatchOperationSequence&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;operations&amp;gt;&lt;br /&gt;
    &amp;lt;li Class=&amp;quot;PatchOperationAdd&amp;quot; MayRequire=&amp;quot;Ludeon.Rimworld.Biotech&amp;quot;&amp;gt; &amp;lt;!-- Only runs if Biotech is active --&amp;gt;&lt;br /&gt;
      &amp;lt;xpath&amp;gt;Defs/ThingDef[defName=&amp;quot;MechGestator&amp;quot;]/recipes&amp;lt;xpath&amp;gt;&lt;br /&gt;
      &amp;lt;value&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;MyCustomMech&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/value&amp;gt;&lt;br /&gt;
    &amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li Class=&amp;quot;PatchOperationAdd&amp;quot; MayRequire=&amp;quot;MyProject.OtherModPackageId&amp;quot;&amp;gt;&amp;lt;!-- Only runs if the specific mod is active --&amp;gt;&lt;br /&gt;
      &amp;lt;xpath&amp;gt;Defs/ThingDef[defName=&amp;quot;OtherModWorkbench&amp;quot;]/recipes&amp;lt;/xpath&amp;gt;&lt;br /&gt;
      &amp;lt;value&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;MyCustomResource&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/value&amp;gt;&lt;br /&gt;
    &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;/operations&amp;gt;&lt;br /&gt;
&amp;lt;/Operation&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; can be used in lieu of PatchOperationFindMod in a PatchOperationSequence.&lt;br /&gt;
&lt;br /&gt;
'''Warning''': PatchOperationSequence can obfuscate errors, so it is strongly recommended that you individually test patches first before you place them in a sequence. Please see [[Modding_Tutorials/PatchOperations|PatchOperations]] for more information.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Def References ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; can be used on any field that references [[Modding_Tutorials/Defs|Defs]] by their &amp;lt;code&amp;gt;defName&amp;lt;/code&amp;gt;, including single fields, lists, and special parsed lists such as stat blocks and item count lists. &lt;br /&gt;
&lt;br /&gt;
'''Note''': When used in direct references or in a list of direct references, Def references have a special behavior in that &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; only serves to suppress cross-resolution errors if the targeted DLC or mod is not loaded. '''If RimWorld finds the targeted Def, it will be loaded into the list regardless of whether the specified DLC or mod is loaded or not.''' This does not apply to special parsed lists (see below).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;TutorialTableWrapper&amp;quot;&amp;gt;&lt;br /&gt;
{| class=&amp;quot;TutorialCodeTable&amp;quot;&lt;br /&gt;
! XML Example !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;LifeStageDef ParentName=&amp;quot;HumanlikeAdolescent&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;HumanlikeBaby&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;baby&amp;lt;/label&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;thinkTreeMainOverride MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;HumanlikeBaby&amp;lt;/thinkTreeMainOverride&amp;gt;&lt;br /&gt;
  &amp;lt;thinkTreeConstantOverride MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;HumanlikeBabyConstant&amp;lt;/thinkTreeConstantOverride&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/LifeStageDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used in human LifeStageDefs for Def references such as the ThinkTreeDefs for certain lifestages. These Defs only exist in the Biotech DLC, and thus must have errors suppressed if the DLC is not loaded.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ThingDef ParentName=&amp;quot;BasePawn&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;Human&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;human&amp;lt;/label&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;recipes&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;ExciseCarcinoma&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;AdministerMechSerumHealer&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;RemoveBodyPart&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Euthanize&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;Anesthetize&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;CureScaria&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;Vasectomy&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;ReverseVasectomy&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;TubalLigation&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;ExtractOvum&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Royalty&amp;quot;&amp;gt;CureBloodRot&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Royalty&amp;quot;&amp;gt;CureAbasia&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;ExtractHemogenPack&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;BloodTransfusion&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;ImplantXenogerm&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;ImplantIUD&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;RemoveIUD&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;TerminatePregnancy&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;/recipes&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ThingDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used in the Human [[Modding_Tutorials/ThingDef|ThingDef]] to set surgery recipes that are only relevant to specific DLCs. Without &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt;, these would cause errors as the specified [[Modding_Tutorials/Defs|Defs]] only exist in their respective DLCs.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ThingDef Name=&amp;quot;TorchLamp&amp;quot; ParentName=&amp;quot;BuildingBase&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;TorchLamp&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;torch lamp&amp;lt;/label&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;statBases&amp;gt;&lt;br /&gt;
    &amp;lt;MaxHitPoints&amp;gt;75&amp;lt;/MaxHitPoints&amp;gt;&lt;br /&gt;
    &amp;lt;WorkToBuild&amp;gt;100&amp;lt;/WorkToBuild&amp;gt;&lt;br /&gt;
    &amp;lt;Flammability&amp;gt;0&amp;lt;/Flammability&amp;gt;&lt;br /&gt;
    &amp;lt;MeditationFocusStrength&amp;gt;0.0&amp;lt;/MeditationFocusStrength&amp;gt;&lt;br /&gt;
    &amp;lt;StyleDominance MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;5&amp;lt;/StyleDominance&amp;gt;&lt;br /&gt;
  &amp;lt;/statBases&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ThingDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; can be used in &amp;quot;special parsers&amp;quot; such as those for &amp;lt;code&amp;gt;statBases&amp;lt;/code&amp;gt;. These are usually used to create shorthand notation wherein the node name is the &amp;lt;code&amp;gt;defName&amp;lt;/code&amp;gt; and the value is the stat value. The vanilla torch lamp uses the &amp;lt;code&amp;gt;StyleDominance&amp;lt;/code&amp;gt; stat from [[Ideology]] to affect the room it is placed in.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ThingDef ParentName=&amp;quot;ApparelMakeableBase&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;Apparel_Duster&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant tags omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;equippedStatOffsets&amp;gt;&lt;br /&gt;
    &amp;lt;SlaveSuppressionOffset MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;-0.05&amp;lt;/SlaveSuppressionOffset&amp;gt;&lt;br /&gt;
  &amp;lt;/equippedStatOffsets&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant tags omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/ThingDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;equippedStatOffsets&amp;lt;/code&amp;gt; is another example of a stat block. In this example, the vanilla [[Duster]] apparel has a [[Slavery|SlaveSuppressionOffset]] stat offset from [[Ideology]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;BiomeDef&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;TemperateForest&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;wildPlants&amp;gt;&lt;br /&gt;
    &amp;lt;Plant_Grass&amp;gt;5.0&amp;lt;/Plant_Grass&amp;gt;&lt;br /&gt;
    &amp;lt;Plant_GrayGrass MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;2&amp;lt;/Plant_GrayGrass&amp;gt;&lt;br /&gt;
    &amp;lt;Plant_TallGrass&amp;gt;2.0&amp;lt;/Plant_TallGrass&amp;gt;&lt;br /&gt;
    &amp;lt;Plant_Brambles&amp;gt;1.0&amp;lt;/Plant_Brambles&amp;gt;&lt;br /&gt;
    &amp;lt;Plant_Ripthorn MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.8&amp;lt;/Plant_Ripthorn&amp;gt;&lt;br /&gt;
    &amp;lt;!-- many entries omitted --&amp;gt;&lt;br /&gt;
  &amp;lt;/wildPlants&amp;gt;&lt;br /&gt;
  &amp;lt;pollutionWildAnimals MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;Toxalope MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.4&amp;lt;/Toxalope&amp;gt;&lt;br /&gt;
    &amp;lt;WasteRat MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.1&amp;lt;/WasteRat&amp;gt;&lt;br /&gt;
    &amp;lt;!-- many entries omitted --&amp;gt;&lt;br /&gt;
  &amp;lt;/pollutionWildAnimals&amp;gt;&lt;br /&gt;
  &amp;lt;!-- irrelevant nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/BiomeDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used in &amp;lt;code&amp;gt;BiomeDef&amp;lt;/code&amp;gt; entries to specify wild plants and animals that are only present in DLCs.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;FactionDef ParentName=&amp;quot;FactionBase&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;Mechanoid&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;mechanoid hive&amp;lt;/label&amp;gt;&lt;br /&gt;
  &amp;lt;pawnGroupMakers&amp;gt;&lt;br /&gt;
    &amp;lt;li&amp;gt;&lt;br /&gt;
      &amp;lt;!-- All types--&amp;gt;&lt;br /&gt;
      &amp;lt;kindDef&amp;gt;Combat&amp;lt;/kindDef&amp;gt;&lt;br /&gt;
      &amp;lt;commonality&amp;gt;100&amp;lt;/commonality&amp;gt;&lt;br /&gt;
      &amp;lt;options&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Scyther&amp;gt;10&amp;lt;/Mech_Scyther&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Pikeman&amp;gt;10&amp;lt;/Mech_Pikeman&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Lancer&amp;gt;10&amp;lt;/Mech_Lancer&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_CentipedeBlaster&amp;gt;10&amp;lt;/Mech_CentipedeBlaster&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Militor MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;20&amp;lt;/Mech_Militor&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Centurion MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;2&amp;lt;/Mech_Centurion&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Warqueen MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;1&amp;lt;/Mech_Warqueen&amp;gt;&lt;br /&gt;
        &amp;lt;Mech_Apocriton MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;1&amp;lt;/Mech_Apocriton&amp;gt;&lt;br /&gt;
      &amp;lt;/options&amp;gt;&lt;br /&gt;
    &amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;!-- many other nodes omitted --&amp;gt;&lt;br /&gt;
  &amp;lt;/pawnGroupMakers&amp;gt;&lt;br /&gt;
  &amp;lt;!-- many other nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/FactionDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used by official content &amp;lt;code&amp;gt;FactionDef&amp;lt;/code&amp;gt; entries to specify &amp;lt;code&amp;gt;PawnKindDef&amp;lt;/code&amp;gt; types that only exist in DLCs.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;FactionDef ParentName=&amp;quot;FactionBase&amp;quot; Name=&amp;quot;OutlanderFactionBase&amp;quot; Abstract=&amp;quot;True&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;disallowedMemes&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;Structure_Animist&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;Nudism&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;li MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;Blindsight&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;/disallowedMemes&amp;gt;&lt;br /&gt;
  &amp;lt;structureMemeWeights&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_TheistEmbodied MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_TheistEmbodied&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_TheistAbstract MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;2&amp;lt;/Structure_TheistAbstract&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_Ideological MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_Ideological&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_Archist MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_Archist&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_OriginChristian MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_OriginChristian&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_OriginIslamic MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_OriginIslamic&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_OriginHindu MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_OriginHindu&amp;gt;&lt;br /&gt;
    &amp;lt;Structure_OriginBuddhist MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot;&amp;gt;1&amp;lt;/Structure_OriginBuddhist&amp;gt;&lt;br /&gt;
  &amp;lt;/structureMemeWeights&amp;gt;&lt;br /&gt;
  &amp;lt;!-- many nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/FactionDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; must be used for meme and precept references in &amp;lt;code&amp;gt;FactionDef&amp;lt;/code&amp;gt; entries, as they only exist if [[Ideology]] is loaded.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;FactionDef ParentName=&amp;quot;FactionBase&amp;quot; Name=&amp;quot;OutlanderFactionBase&amp;quot; Abstract=&amp;quot;True&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;xenotypeSet&amp;gt;&lt;br /&gt;
    &amp;lt;xenotypeChances&amp;gt;&lt;br /&gt;
      &amp;lt;Hussar MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.05&amp;lt;/Hussar&amp;gt;&lt;br /&gt;
      &amp;lt;Dirtmole MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.05&amp;lt;/Dirtmole&amp;gt;&lt;br /&gt;
      &amp;lt;Genie MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.025&amp;lt;/Genie&amp;gt;&lt;br /&gt;
      &amp;lt;Neanderthal MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;0.025&amp;lt;/Neanderthal&amp;gt;&lt;br /&gt;
    &amp;lt;/xenotypeChances&amp;gt;&lt;br /&gt;
  &amp;lt;/xenotypeSet&amp;gt;&lt;br /&gt;
  &amp;lt;!-- many nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/FactionDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; must be used for xenotype references in &amp;lt;code&amp;gt;FactionDef&amp;lt;/code&amp;gt; entries, as they only exist if [[Biotech]] is loaded.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Optional Defs ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; can be used to conditionally load entire Defs. This is far easier to use than the prior option of using [[Modding_Tutorials/PatchOperations|PatchOperations]] to inject new Defs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;TutorialTableWrapper&amp;quot;&amp;gt;&lt;br /&gt;
{| class=&amp;quot;TutorialCodeTable&amp;quot;&lt;br /&gt;
! XML !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ThingDef MayRequire=&amp;quot;Ludeon.RimWorld.Ideology&amp;quot; ParentName=&amp;quot;Brazier&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;DarklightBrazier&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;darklight brazier&amp;lt;/label&amp;gt;&lt;br /&gt;
  &amp;lt;description&amp;gt;A specially treated brazier that illuminates its surroundings with darklight and creates heat. These satisfy royal brazier requirements.&amp;lt;/description&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant content omitted --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ThingDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used to activate [[Royalty]]'s darklight brazier stat if [[Ideology]] is also loaded.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;StatDef ParentName=&amp;quot;MeditationFocusBase&amp;quot; MayRequireAnyOf=&amp;quot;Ludeon.RimWorld.Royalty,Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;MeditationFocusGain&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;meditation psyfocus gain&amp;lt;/label&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant content omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/StatDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequireAnyOf&amp;lt;/code&amp;gt; is used to activate the MeditationFocusGain stat if either [[Royalty]] or [[Biotech]] is loaded.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ThingDef ParentName=&amp;quot;ApparelMakeableBase&amp;quot; MayRequireAnyOf=&amp;quot;Ludeon.RimWorld.Royalty,Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;Apparel_Cape&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;cape&amp;lt;/label&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;!-- irrelevant content omitted --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ThingDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequireAnyOf&amp;lt;/code&amp;gt; is used to activate the [[Cape]] apparel if either [[Royalty]] or [[Biotech]] is loaded.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous ==&lt;br /&gt;
&lt;br /&gt;
The following are specific, non-standard usages of &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; from official content XML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;TutorialTableWrapper&amp;quot;&amp;gt;&lt;br /&gt;
{| class=&amp;quot;TutorialCodeTable&amp;quot;&lt;br /&gt;
! XML !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;PawnKindDef Abstract=&amp;quot;True&amp;quot; Name=&amp;quot;BasePlayerPawnKind&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;race&amp;gt;Human&amp;lt;/race&amp;gt;&lt;br /&gt;
  &amp;lt;apparelIgnorePollution MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;true&amp;lt;/apparelIgnorePollution&amp;gt;&lt;br /&gt;
  &amp;lt;!-- many nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/PawnKindDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used by &amp;lt;code&amp;gt;PawnKindDef&amp;lt;/code&amp;gt; for the &amp;lt;code&amp;gt;apparelIgnorePollution&amp;lt;/code&amp;gt; node.&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': Analysis of game code seems to indicate that this particular use of &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; does not actually have any effect, and may have been put here merely as a flag.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;LifeStageDef ParentName=&amp;quot;HumanlikeAdolescent&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;HumanlikeBaby&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;baby&amp;lt;/label&amp;gt;&lt;br /&gt;
  &amp;lt;thinkTreeMainOverride MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;HumanlikeBaby&amp;lt;/thinkTreeMainOverride&amp;gt;&lt;br /&gt;
  &amp;lt;thinkTreeConstantOverride MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;HumanlikeBabyConstant&amp;lt;/thinkTreeConstantOverride&amp;gt;&lt;br /&gt;
  &amp;lt;!-- many nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/LifeStageDef&amp;gt;&lt;br /&gt;
&amp;lt;LifeStageDef Name=&amp;quot;LifeStageHumanlikeChild&amp;quot; ParentName=&amp;quot;HumanlikeAdolescent&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;defName&amp;gt;HumanlikeChild&amp;lt;/defName&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;child&amp;lt;/label&amp;gt;&lt;br /&gt;
  &amp;lt;workerClass MayRequire=&amp;quot;Ludeon.RimWorld.Biotech&amp;quot;&amp;gt;LifeStageWorker_HumanlikeChild&amp;lt;/workerClass&amp;gt;&lt;br /&gt;
  &amp;lt;!-- many nodes omitted --&amp;gt;&lt;br /&gt;
&amp;lt;/LifeStageDef&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
| class=&amp;quot;TutorialCodeTable-description&amp;quot; |&lt;br /&gt;
&amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; is used for think tree override and worker classes in &amp;lt;code&amp;gt;LifeStageDef&amp;lt;/code&amp;gt; entries for functionality related to children and growth in [[Biotech]].&lt;br /&gt;
&lt;br /&gt;
'''NOTE''': Analysis of game code seems to indicate that the latter use of &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; does not actually have any effect, and may have been put here merely as a flag. The WorkerClass code is itself locked to [[Biotech]].&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt; does '''NOT''' work on top-level XML nodes that are not &amp;lt;code&amp;gt;Def&amp;lt;/code&amp;gt;s. This means that &amp;lt;code&amp;gt;Operation&amp;lt;/code&amp;gt; tags cannot use &amp;lt;code&amp;gt;MayRequire&amp;lt;/code&amp;gt;, though as previous indicated, &amp;lt;code&amp;gt;PatchOperationSequence&amp;lt;/code&amp;gt; lists can. Again, this is not recommended because &amp;lt;code&amp;gt;PatchOperationSequence&amp;lt;/code&amp;gt; can obfuscate errors.&lt;br /&gt;
* There is a bug in RimWorld versions before 1.6 where MayRequire does not ignore the &amp;lt;code&amp;gt;_steam&amp;lt;/code&amp;gt; suffix appended to Steam copies of a mod when you have both a local mod and a Steam mod with the exact same packageId. This means that if you have a local copy, then only the local copy will be recognized by MayRequire. You can get around this in the meantime by using &amp;lt;code&amp;gt;MayRequireAnyOf&amp;lt;/code&amp;gt; with both packageIds, i.e. &amp;lt;code&amp;gt;MayRequireAnyOf=&amp;quot;MyName.MyMod,MyName.MyMod_steam&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Category:Modding]]&lt;br /&gt;
[[Category:Modding tutorials]]&lt;/div&gt;</summary>
		<author><name>CryptikLemur</name></author>
	</entry>
	<entry>
		<id>https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181166</id>
		<title>Modding Tutorials/Asset Bundles</title>
		<link rel="alternate" type="text/html" href="https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181166"/>
		<updated>2026-06-25T23:08:19Z</updated>

		<summary type="html">&lt;p&gt;CryptikLemur: Adding information on texture load priority&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Asset Bundles}}&lt;br /&gt;
&lt;br /&gt;
{{BackToTutorials}}&lt;br /&gt;
&lt;br /&gt;
{{:Modding_Tutorials/Under_Review}}&lt;br /&gt;
&lt;br /&gt;
This tutorial explains what asset bundles are, why you might want them, and how they compare to the two other ways RimWorld can load custom art: loose textures and DDS files. It starts from zero, so you do not need to know anything about Unity going in. The later sections branch into the actual build process and then into the deeper stuff: shaders, sounds, fonts, and the things that go wrong.&lt;br /&gt;
&lt;br /&gt;
== What is an asset bundle? ==&lt;br /&gt;
&lt;br /&gt;
An asset bundle is a single file that holds a bunch of Unity assets already packed in the format the engine wants. RimWorld runs on Unity, and Unity has its own internal way of storing textures, meshes, shaders, audio, and so on. A loose PNG sitting in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder is not in that format yet, so the game has to convert it before it can use it.&lt;br /&gt;
&lt;br /&gt;
A bundle is that conversion done ahead of time, saved to disk, and handed to the engine in one piece. The rest of this page is mostly consequences of that.&lt;br /&gt;
&lt;br /&gt;
== Why would I care? ==&lt;br /&gt;
&lt;br /&gt;
Two reasons, mostly:&lt;br /&gt;
&lt;br /&gt;
'''Load time.''' When you ship loose PNGs, RimWorld converts every single one while the game boots (more on this in the next section). For a small mod you will never notice. For a mod with hundreds or thousands of textures, that conversion adds up, and it is part of why a heavily-modded load screen sits there for a while. A bundle skips the conversion because the work is already done.&lt;br /&gt;
&lt;br /&gt;
'''Everything that is not a texture.''' Loose files only really work for textures and sounds. If you want to ship a custom shader, a custom font, a custom mesh, or anything else Unity-shaped, a bundle is the normal way to get it into the game. There is no &amp;lt;code&amp;gt;Shaders/&amp;lt;/code&amp;gt; folder you can just drop a file into.&lt;br /&gt;
&lt;br /&gt;
If your mod is XML and a handful of textures, you probably do not need bundles at all. If your mod is large, or it needs a custom shader or font, you do.&lt;br /&gt;
&lt;br /&gt;
As of 1.6, bundles are the encouraged path for anything beyond a small loose-texture mod. The engine grew real first-class support for them: it loads them for you, swaps the right one per operating system, and serves their contents through the same content lookup you already use. Earlier versions made you wire all of that up by hand, which is why older tutorials look more involved than this one. On 1.6+ most of the friction is gone.&lt;br /&gt;
&lt;br /&gt;
== Loose textures vs DDS vs asset bundles ==&lt;br /&gt;
&lt;br /&gt;
All three of these get a texture onto the screen. They differ in '''when''' the work happens and '''what''' they can carry. Mix those two questions up and the whole topic gets confusing fast.&lt;br /&gt;
&lt;br /&gt;
=== Loose textures (PNG) ===&lt;br /&gt;
&lt;br /&gt;
You put a &amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt; in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder and reference it by path. Simplest possible setup, and what almost every tutorial starts with. (The [[Modding_Tutorials/Textures|Textures]] page covers the path rules and texture conventions in full; this page assumes you have that down.)&lt;br /&gt;
&lt;br /&gt;
RimWorld does not render your PNG directly. On load it converts the PNG into a compressed GPU texture (the same family of formats DDS uses) and caches the result. So the PNG is a source file, not the thing the GPU actually draws. That conversion is cheap for one texture and not cheap for a thousand.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' dead simple, easy to edit, easy to diff, no tooling.&lt;br /&gt;
* '''Con:''' the game pays a conversion cost on load, and you cannot ship anything other than textures and sounds this way.&lt;br /&gt;
&lt;br /&gt;
=== DDS textures ===&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;code&amp;gt;.dds&amp;lt;/code&amp;gt; file is a texture that is ''already'' in a GPU-ready compressed format. If you drop a DDS next to where the PNG would go, RimWorld uses it directly and skips the conversion step entirely.&lt;br /&gt;
&lt;br /&gt;
So the loose-vs-DDS question is mostly about that conversion cost, not about how the final image looks. A correctly-made DDS and the game's own PNG conversion land in roughly the same place visually. What DDS buys you is a faster load and no first-load hitch, because the expensive part already happened on your machine when you made the file.&lt;br /&gt;
&lt;br /&gt;
The catch is you have to ''make'' the DDS, and if you make it wrong (wrong compression, no mipmaps, wrong color space) it can look worse than just letting the game convert the PNG. It is a real win for large texture sets, and a footgun for people who do not know what BC7 or mipmaps are.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' no runtime conversion, faster load, no first-load stutter for that texture.&lt;br /&gt;
* '''Con:''' you have to generate it correctly, harder to edit after the fact, much larger on disk than the source PNGs, and still textures-only.&lt;br /&gt;
&lt;br /&gt;
=== Asset bundles ===&lt;br /&gt;
&lt;br /&gt;
A bundle is the next step up. It is one file that can hold many textures (already in GPU format, like DDS, so same load-time win) ''plus'' meshes, shaders, fonts, and audio. Instead of hundreds of loose files converted one by one, the engine loads one bundle and everything inside is ready to go.&lt;br /&gt;
&lt;br /&gt;
The trade is that a bundle is opaque. You cannot open it in an image editor and tweak a pixel. You rebuild it from your source assets, which means you keep your editable PNGs and Unity sources somewhere and treat the bundle as a build output, the same way you treat a compiled DLL.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' fastest load for large asset sets, one file instead of thousands, and it is the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
* '''Con:''' you need a build step and the Unity tooling to produce it, and the output is not hand-editable.&lt;br /&gt;
&lt;br /&gt;
=== Quick comparison ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Approach !! Load cost !! Disk size !! Can carry !! Editable after build? !! Good for&lt;br /&gt;
|-&lt;br /&gt;
| Loose PNG || Converted every load || Small || Textures, sounds || Yes || Small mods, prototyping&lt;br /&gt;
|-&lt;br /&gt;
| DDS || None (pre-converted) || Large || Textures || Painful || Large texture sets&lt;br /&gt;
|-&lt;br /&gt;
| Asset bundle || None (pre-converted) || Smallest || Textures, meshes, shaders, fonts, audio || No (rebuild from source) || Large mods, anything non-texture&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
That disk-size column is worth some real numbers. Take one texture set of around 960 files and store it all three ways:&lt;br /&gt;
&lt;br /&gt;
* As loose PNGs: about 40 MB.&lt;br /&gt;
* Converted to DDS: about 230 MB. The conversion skips the runtime work but blows up the size on disk.&lt;br /&gt;
* Packed into a single asset bundle: about 28 MB.&lt;br /&gt;
&lt;br /&gt;
So DDS is the ''largest'' on disk, not the smallest. It trades space for skipping the load-time conversion. The bundle gets that same load-time win and still ends up the smallest of the three, because it is compressed into one file. (Numbers from a comparison shared in the [https://discord.com/channels/214523379766525963/632790371256238120/1405733585268375643 RimWorld modding Discord]; your exact figures will vary with the texture set and compression settings.)&lt;br /&gt;
&lt;br /&gt;
== The workflow, in plain terms ==&lt;br /&gt;
&lt;br /&gt;
Before any code or folder layout, here is what building a bundle actually looks like from a height:&lt;br /&gt;
&lt;br /&gt;
# You keep your real, editable source assets (PNGs, Unity materials, shader files, font files) in a Unity project.&lt;br /&gt;
# In that Unity project you tag each asset with a bundle name, then tell Unity to build the bundles.&lt;br /&gt;
# Unity spits out the bundle files.&lt;br /&gt;
# You ship those bundle files inside your RimWorld mod.&lt;br /&gt;
# At game load, RimWorld finds the bundle on its own and serves its contents through the normal content lookup (1.6 does the loading for you; textures and sounds resolve by their existing path, while shaders and fonts you still fetch by name in code).&lt;br /&gt;
&lt;br /&gt;
That is the loop. You edit in Unity, build, copy the output into your mod, and the game picks it up. The first time through it feels like a lot of ceremony for a texture. Once it is set up, adding a new asset is just &amp;quot;drop it in the Unity project, rebuild, copy.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
You do not have to do every step by hand. The next section covers that before the build details, so you can go find a tool first if you would rather not set this up yourself.&lt;br /&gt;
&lt;br /&gt;
== Before we get started: Do I actually have to do everything below? ==&lt;br /&gt;
&lt;br /&gt;
No. Setting up the Unity project, writing the build script, and juggling per-platform bundles is fiddly enough that several modders have written tools to do it for you. They wrap the whole thing, handle the multi-platform build, and spit out ready-to-ship bundles without you babysitting the Unity editor. If that sounds like what you want, you can stop here and go find one; the rest of this page is for when you want to understand what those tools are doing or build it yourself.&lt;br /&gt;
&lt;br /&gt;
This tutorial does not point at any specific one on purpose, since they come and go and get superseded. A search on Google or GitHub for RimWorld asset bundle tooling turns them up, and the modding Discord communities are usually the fastest place to find out which one people are actually using right now and which one is abandoned. Ask there before sinking an afternoon into a tool that got replaced six months ago.&lt;br /&gt;
&lt;br /&gt;
== Branch: the real build steps ==&lt;br /&gt;
&lt;br /&gt;
If you want the actual mechanics, here they are. First, install the right Unity editor. This matters more than anything else on this page: a bundle built on the wrong Unity version may silently fail to load. RimWorld 1.6 runs on '''Unity 2022.3.35f1''', so install exactly that version through the Unity Hub (under &amp;quot;Add&amp;quot; you can pick a specific archived version, or grab it from the Unity download archive). Build your bundles in 2022.3.35f1 and they line up with what the game expects. If RimWorld updates to a new Unity version down the line, you rebuild against the new one.&lt;br /&gt;
&lt;br /&gt;
=== Folder layout ===&lt;br /&gt;
&lt;br /&gt;
Inside your mod, bundles usually live somewhere like:&lt;br /&gt;
&lt;br /&gt;
 MyMod/&lt;br /&gt;
   About/&lt;br /&gt;
   Defs/&lt;br /&gt;
   Textures/&lt;br /&gt;
   AssetBundles/&lt;br /&gt;
     mymod_assets_win&lt;br /&gt;
     mymod_assets_mac&lt;br /&gt;
     mymod_assets_linux&lt;br /&gt;
&lt;br /&gt;
The bundle file itself has no extension. You name it whatever you tagged it in Unity, with the OS suffix on the end (see the deep dive for why the three files). There is no &amp;lt;code&amp;gt;.dll&amp;lt;/code&amp;gt; required to use a bundle: a pure XML-and-textures mod can ship a bundle and never write a line of C#, because the game loads it and serves its contents automatically.&lt;br /&gt;
&lt;br /&gt;
=== Tagging and building in Unity ===&lt;br /&gt;
&lt;br /&gt;
In the Unity project, select an asset, and at the bottom of the Inspector there is an AssetBundle dropdown. Assign a bundle name there. Everything sharing that name builds into the same bundle.&lt;br /&gt;
&lt;br /&gt;
Then you need a small editor script to actually trigger the build, because Unity does not expose bundle building in the default menus.&lt;br /&gt;
&lt;br /&gt;
The bare version is just one &amp;lt;code&amp;gt;BuildPipeline.BuildAssetBundles&amp;lt;/code&amp;gt; call. It works, but it ships your textures with whatever import settings they happened to have, which is usually uncompressed and wasteful. Controlling how each texture imports before it gets packed is what actually shrinks the disk and memory footprint, so the script below does two things: it walks your textures and sets sane import settings on each, then builds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using UnityEditor;&lt;br /&gt;
using UnityEngine;&lt;br /&gt;
using System.IO;&lt;br /&gt;
&lt;br /&gt;
public static class BundleBuilder&lt;br /&gt;
{&lt;br /&gt;
    [MenuItem(&amp;quot;Assets/Build AssetBundles&amp;quot;)]&lt;br /&gt;
    static void Build()&lt;br /&gt;
    {&lt;br /&gt;
        // First pass: set good import settings on every texture in the project.&lt;br /&gt;
        foreach (string guid in AssetDatabase.FindAssets(&amp;quot;t:Texture2D&amp;quot;))&lt;br /&gt;
        {&lt;br /&gt;
            string assetPath = AssetDatabase.GUIDToAssetPath(guid);&lt;br /&gt;
            if (AssetImporter.GetAtPath(assetPath) is not TextureImporter tex) continue;&lt;br /&gt;
&lt;br /&gt;
            string lower = assetPath.ToLower();&lt;br /&gt;
            bool isTerrain = lower.Contains(&amp;quot;/terrain/&amp;quot;);&lt;br /&gt;
            bool isMask    = lower.Contains(&amp;quot;_mask&amp;quot;);&lt;br /&gt;
            bool isNormal  = lower.Contains(&amp;quot;_normal&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            // GUI type suits RimWorld's flat 2D art and avoids Unity generating sprite sub-assets.&lt;br /&gt;
            tex.textureType       = isNormal ? TextureImporterType.NormalMap : TextureImporterType.GUI;&lt;br /&gt;
            tex.alphaIsTransparency = true;&lt;br /&gt;
&lt;br /&gt;
            // Mask and normal maps hold raw data, not color, so they must stay linear (sRGB off).&lt;br /&gt;
            tex.sRGBTexture = !isMask &amp;amp;&amp;amp; !isNormal;&lt;br /&gt;
&lt;br /&gt;
            // Terrain tiles repeat across the ground; everything else should clamp at its edges.&lt;br /&gt;
            tex.wrapMode  = isTerrain ? TextureWrapMode.Repeat : TextureWrapMode.Clamp;&lt;br /&gt;
            tex.anisoLevel = isTerrain ? 8 : 1;&lt;br /&gt;
&lt;br /&gt;
            tex.filterMode    = FilterMode.Trilinear;&lt;br /&gt;
            tex.mipmapEnabled = true;&lt;br /&gt;
&lt;br /&gt;
            // BC7 high-quality compression: small on disk and in VRAM, good for hand-painted art.&lt;br /&gt;
            tex.SetPlatformTextureSettings(new TextureImporterPlatformSettings&lt;br /&gt;
            {&lt;br /&gt;
                name       = &amp;quot;Standalone&amp;quot;,&lt;br /&gt;
                overridden = true,&lt;br /&gt;
                format     = TextureImporterFormat.BC7,&lt;br /&gt;
                maxTextureSize = 4096,&lt;br /&gt;
                textureCompression = TextureImporterCompression.CompressedHQ&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            tex.SaveAndReimport();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Second pass: build the bundles.&lt;br /&gt;
        string output = &amp;quot;Assets/BuiltBundles&amp;quot;;&lt;br /&gt;
        Directory.CreateDirectory(output);&lt;br /&gt;
        BuildPipeline.BuildAssetBundles(&lt;br /&gt;
            output,&lt;br /&gt;
            BuildAssetBundleOptions.ChunkBasedCompression, // LZ4&lt;br /&gt;
            BuildTarget.StandaloneWindows64&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A few notes on what that import pass is doing:&lt;br /&gt;
&lt;br /&gt;
* '''BC7 + CompressedHQ''' is the compression that gets you the small disk and VRAM footprint. Without it the texture sits in memory uncompressed.&lt;br /&gt;
* '''sRGB off for masks and normal maps.''' A stuff-color mask or a normal map stores data, not a picture, so it must be read as linear. Leaving sRGB on silently corrupts it. The script keys off &amp;lt;code&amp;gt;_mask&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;_normal&amp;lt;/code&amp;gt; in the filename, so name your files accordingly.&lt;br /&gt;
* '''Terrain repeats, everything else clamps.''' Terrain tiles need &amp;lt;code&amp;gt;Repeat&amp;lt;/code&amp;gt; wrap so they tile seamlessly across the ground; a regular sprite wants &amp;lt;code&amp;gt;Clamp&amp;lt;/code&amp;gt; so its edges do not bleed. The script keys off a &amp;lt;code&amp;gt;/Terrain/&amp;lt;/code&amp;gt; folder in the path.&lt;br /&gt;
* '''Mipmaps on''' so the texture does not shimmer when the camera zooms out.&lt;br /&gt;
&lt;br /&gt;
This is a trimmed-down version of what dedicated bundle-building tools do; they also handle audio compression, font atlas generation, and per-bundle include/exclude rules. The texture settings above are the ones worth knowing by hand. Run the menu item, Unity reimports the textures and writes the bundles to the output folder, and you copy the ones you care about into your mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
=== Loading the bundle in your mod ===&lt;br /&gt;
&lt;br /&gt;
In 1.6 you do not load the bundle yourself at all, which catches people off guard since the older docs make it look involved. RimWorld scans every active mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder on startup, loads what it finds, and keeps the loaded bundles on the mod. You do not write &amp;lt;code&amp;gt;AssetBundle.LoadFromFile&amp;lt;/code&amp;gt;, you do not call &amp;lt;code&amp;gt;LoadAsset&amp;lt;/code&amp;gt;, you do not run anything in a static constructor.&lt;br /&gt;
&lt;br /&gt;
The game's normal content lookup also checks bundles for you. When you ask for a texture the usual way, with &amp;lt;code&amp;gt;ContentFinder&amp;lt;Texture2D&amp;gt;.Get(&amp;quot;Things/Item/MyThing&amp;quot;)&amp;lt;/code&amp;gt;, RimWorld walks three places in order: first it looks for a loose file at that path in any active mod, then it falls back to the base game's built-in resources, and only if both miss does it look inside your loaded bundles at the matching path. Same call, same path string, whether the texture is loose or bundled. The asset just has to live at the same path inside the bundle that the loose file would have used (so an asset packed at &amp;lt;code&amp;gt;Textures/Things/Item/MyThing&amp;lt;/code&amp;gt; resolves for the path above).&lt;br /&gt;
&lt;br /&gt;
The practical upshot: for textures and sounds you do not change a single line of your mod's code to switch from loose to bundled. You reference them by path like you always did. You only build a bundle, drop it in the folder, and the game finds the contents.&lt;br /&gt;
&lt;br /&gt;
'''A bundle cannot overwrite or retexture a base game texture.''' Because bundles are checked last, after the base game's own resources, the vanilla texture is always found first and your bundled version at the same path is never reached. Bundles are for adding new content, not replacing what ships with the game. If you want to retexture vanilla art, you still do it the old way: a loose file at the matching path (which is checked before the base resources), or a def or &amp;lt;code&amp;gt;texPath&amp;lt;/code&amp;gt; redirect that points the vanilla thing at your own texture. The same goes for sounds.&lt;br /&gt;
&lt;br /&gt;
The exception is shaders. The content lookup skips the loose-file step for shaders entirely (there is no loose-shader path), so a shader is only ever found inside a bundle. &amp;lt;code&amp;gt;ContentFinder&amp;lt;Shader&amp;gt;.Get(&amp;quot;MyShaderName&amp;quot;)&amp;lt;/code&amp;gt; works, but only because the shader is in a bundle for it to find.&lt;br /&gt;
&lt;br /&gt;
== Branch: the deep dive ==&lt;br /&gt;
&lt;br /&gt;
Most of this only comes up once you are shipping a shader or a font. A small texture-only mod can skip the whole section.&lt;br /&gt;
&lt;br /&gt;
=== Shaders and the cross-platform problem ===&lt;br /&gt;
&lt;br /&gt;
A compiled shader is platform-specific, which is why a lot of mods end up shipping multiple bundles. A shader built for DirectX (Windows) is not the same bytes as one built for Metal (Mac) or Vulkan/OpenGL (Linux). If you build one bundle on Windows and ship it, your shader works on Windows and quietly fails on Mac and Linux, usually showing up as bright magenta where the texture should be.&lt;br /&gt;
&lt;br /&gt;
The fix is to build a bundle per platform, and 1.6 makes this almost free because the game does the picking for you. Name your bundle files with the suffix the game looks for:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_win&amp;lt;/code&amp;gt; loads on Windows&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_mac&amp;lt;/code&amp;gt; loads on Mac&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_linux&amp;lt;/code&amp;gt; loads on Linux&lt;br /&gt;
&lt;br /&gt;
The game checks the running platform and loads only the matching one. A bundle with no suffix at all loads on every platform, which is fine for textures and meshes that do not care, but not safe for shaders. So you build the same assets three times in Unity with different &amp;lt;code&amp;gt;BuildTarget&amp;lt;/code&amp;gt; values (&amp;lt;code&amp;gt;StandaloneWindows64&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneOSX&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneLinux64&amp;lt;/code&amp;gt;), give each the right suffix, and ship all three. You write no platform-detection code yourself.&lt;br /&gt;
&lt;br /&gt;
If you have ever installed a mod that worked fine for everyone except the Mac and Linux players who saw pink everywhere, this is almost always why.&lt;br /&gt;
&lt;br /&gt;
=== Sounds ===&lt;br /&gt;
&lt;br /&gt;
Audio can go in a bundle as an &amp;lt;code&amp;gt;AudioClip&amp;lt;/code&amp;gt;, and for a lot of sounds it is fine and saves you the loose-file management. RimWorld can also load loose audio through its normal sound def system, so for ordinary sound effects you often do not need a bundle at all. Where bundles help is when you want the audio packed with everything else, or when you are loading clips directly from code rather than through a SoundDef.&lt;br /&gt;
&lt;br /&gt;
=== Fonts ===&lt;br /&gt;
&lt;br /&gt;
If you want a custom font in your UI, a bundle is the way. You import the font into Unity, build it into a bundle, load it as a &amp;lt;code&amp;gt;Font&amp;lt;/code&amp;gt;, and assign it where you draw text. There is no loose-font path in RimWorld at all, so here the bundle is not a speed-up, it is the only way in.&lt;br /&gt;
&lt;br /&gt;
=== Meshes ===&lt;br /&gt;
&lt;br /&gt;
Same story as fonts. Custom 3D meshes (not the usual flat sprites, actual geometry) come in through a bundle as a &amp;lt;code&amp;gt;Mesh&amp;lt;/code&amp;gt;. Most RimWorld mods never touch this, but if you are doing something with real geometry it is here.&lt;br /&gt;
&lt;br /&gt;
=== Compression and a couple of gotchas ===&lt;br /&gt;
&lt;br /&gt;
A few things worth knowing before you spend an afternoon confused:&lt;br /&gt;
&lt;br /&gt;
* '''Unity version mismatch.''' A bundle built on a different Unity version than the game uses may refuse to load, often with no clear error, the asset just comes back null. Build on Unity 2022.3.35f1 for RimWorld 1.6. This is the single most common reason a bundle that worked in the editor does nothing in-game.&lt;br /&gt;
* '''Bundle file compression.''' This is how the bundle file is packed on disk, separate from texture compression below. Bundles can be built uncompressed, LZ4, or LZMA. LZ4 is the usual sweet spot: small enough, and it does not stall on load the way LZMA can. &amp;lt;code&amp;gt;BuildAssetBundleOptions.ChunkBasedCompression&amp;lt;/code&amp;gt; gives you LZ4.&lt;br /&gt;
* '''In-memory texture compression and the white-outline bug.''' Loose textures are compressed in memory once loaded, and that compression is what produces the faint white halo people sometimes see around a sprite (the art program threw away color data in fully transparent pixels). A bundle lets you turn that in-memory compression off for its textures, which sidesteps the halo without the usual workaround of padding the outline. The [[Modding_Tutorials/Textures|Textures]] page documents the bug and the loose-texture fix.&lt;br /&gt;
* '''Shader stripping.''' Unity sometimes strips shader variants it thinks are unused during the build, and then your shader looks wrong at runtime because the variant you needed got cut. If a shader misbehaves only in the built bundle but works in the editor, this is a likely suspect.&lt;br /&gt;
* '''Color space.''' If your textures look washed out or too dark coming out of a bundle, check that the import settings and color space match what the game expects. This is the same class of problem that makes a hand-rolled DDS look wrong.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
* Loose PNG: simplest, but converted on every load and limited to textures and sounds.&lt;br /&gt;
* DDS: a texture pre-converted to GPU format, so it skips the load-time conversion. Same final look, faster load, but you have to make it right, and it lands much larger on disk than the source PNGs.&lt;br /&gt;
* Asset bundle: one pre-packed file that carries textures, meshes, shaders, fonts, and audio. The fastest option for big mods and the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
&lt;br /&gt;
If your mod is small, stay loose. If it is large or needs anything Unity-shaped beyond a texture, learn bundles, and on 1.6+ that is the encouraged path anyway since the engine does the loading and per-OS swapping for you. And if you are shipping a shader, build one bundle per platform with the right suffix, or your Mac and Linux players get magenta.&lt;br /&gt;
&lt;br /&gt;
[[Category:Modding]]&lt;br /&gt;
[[Category:Modding tutorials]]&lt;/div&gt;</summary>
		<author><name>CryptikLemur</name></author>
	</entry>
	<entry>
		<id>https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181163</id>
		<title>Modding Tutorials/Asset Bundles</title>
		<link rel="alternate" type="text/html" href="https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181163"/>
		<updated>2026-06-25T22:55:13Z</updated>

		<summary type="html">&lt;p&gt;CryptikLemur: Adding summary info about DDS size&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Asset Bundles}}&lt;br /&gt;
&lt;br /&gt;
{{BackToTutorials}}&lt;br /&gt;
&lt;br /&gt;
This tutorial explains what asset bundles are, why you might want them, and how they compare to the two other ways RimWorld can load custom art: loose textures and DDS files. It starts from zero, so you do not need to know anything about Unity going in. The later sections branch into the actual build process and then into the deeper stuff: shaders, sounds, fonts, and the things that go wrong.&lt;br /&gt;
&lt;br /&gt;
== What is an asset bundle? ==&lt;br /&gt;
&lt;br /&gt;
An asset bundle is a single file that holds a bunch of Unity assets already packed in the format the engine wants. RimWorld runs on Unity, and Unity has its own internal way of storing textures, meshes, shaders, audio, and so on. A loose PNG sitting in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder is not in that format yet, so the game has to convert it before it can use it.&lt;br /&gt;
&lt;br /&gt;
A bundle is that conversion done ahead of time, saved to disk, and handed to the engine in one piece. The rest of this page is mostly consequences of that.&lt;br /&gt;
&lt;br /&gt;
== Why would I care? ==&lt;br /&gt;
&lt;br /&gt;
Two reasons, mostly:&lt;br /&gt;
&lt;br /&gt;
'''Load time.''' When you ship loose PNGs, RimWorld converts every single one while the game boots (more on this in the next section). For a small mod you will never notice. For a mod with hundreds or thousands of textures, that conversion adds up, and it is part of why a heavily-modded load screen sits there for a while. A bundle skips the conversion because the work is already done.&lt;br /&gt;
&lt;br /&gt;
'''Everything that is not a texture.''' Loose files only really work for textures and sounds. If you want to ship a custom shader, a custom font, a custom mesh, or anything else Unity-shaped, a bundle is the normal way to get it into the game. There is no &amp;lt;code&amp;gt;Shaders/&amp;lt;/code&amp;gt; folder you can just drop a file into.&lt;br /&gt;
&lt;br /&gt;
If your mod is XML and a handful of textures, you probably do not need bundles at all. If your mod is large, or it needs a custom shader or font, you do.&lt;br /&gt;
&lt;br /&gt;
As of 1.6, bundles are the encouraged path for anything beyond a small loose-texture mod. The engine grew real first-class support for them: it loads them for you, swaps the right one per operating system, and serves their contents through the same content lookup you already use. Earlier versions made you wire all of that up by hand, which is why older tutorials look more involved than this one. On 1.6+ most of the friction is gone.&lt;br /&gt;
&lt;br /&gt;
== Loose textures vs DDS vs asset bundles ==&lt;br /&gt;
&lt;br /&gt;
All three of these get a texture onto the screen. They differ in '''when''' the work happens and '''what''' they can carry. Mix those two questions up and the whole topic gets confusing fast.&lt;br /&gt;
&lt;br /&gt;
=== Loose textures (PNG) ===&lt;br /&gt;
&lt;br /&gt;
You put a &amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt; in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder and reference it by path. Simplest possible setup, and what almost every tutorial starts with. (The [[Modding_Tutorials/Textures|Textures]] page covers the path rules and texture conventions in full; this page assumes you have that down.)&lt;br /&gt;
&lt;br /&gt;
RimWorld does not render your PNG directly. On load it converts the PNG into a compressed GPU texture (the same family of formats DDS uses) and caches the result. So the PNG is a source file, not the thing the GPU actually draws. That conversion is cheap for one texture and not cheap for a thousand.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' dead simple, easy to edit, easy to diff, no tooling.&lt;br /&gt;
* '''Con:''' the game pays a conversion cost on load, and you cannot ship anything other than textures and sounds this way.&lt;br /&gt;
&lt;br /&gt;
=== DDS textures ===&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;code&amp;gt;.dds&amp;lt;/code&amp;gt; file is a texture that is ''already'' in a GPU-ready compressed format. If you drop a DDS next to where the PNG would go, RimWorld uses it directly and skips the conversion step entirely.&lt;br /&gt;
&lt;br /&gt;
So the loose-vs-DDS question is mostly about that conversion cost, not about how the final image looks. A correctly-made DDS and the game's own PNG conversion land in roughly the same place visually. What DDS buys you is a faster load and no first-load hitch, because the expensive part already happened on your machine when you made the file.&lt;br /&gt;
&lt;br /&gt;
The catch is you have to ''make'' the DDS, and if you make it wrong (wrong compression, no mipmaps, wrong color space) it can look worse than just letting the game convert the PNG. It is a real win for large texture sets, and a footgun for people who do not know what BC7 or mipmaps are.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' no runtime conversion, faster load, no first-load stutter for that texture.&lt;br /&gt;
* '''Con:''' you have to generate it correctly, harder to edit after the fact, much larger on disk than the source PNGs, and still textures-only.&lt;br /&gt;
&lt;br /&gt;
=== Asset bundles ===&lt;br /&gt;
&lt;br /&gt;
A bundle is the next step up. It is one file that can hold many textures (already in GPU format, like DDS, so same load-time win) ''plus'' meshes, shaders, fonts, and audio. Instead of hundreds of loose files converted one by one, the engine loads one bundle and everything inside is ready to go.&lt;br /&gt;
&lt;br /&gt;
The trade is that a bundle is opaque. You cannot open it in an image editor and tweak a pixel. You rebuild it from your source assets, which means you keep your editable PNGs and Unity sources somewhere and treat the bundle as a build output, the same way you treat a compiled DLL.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' fastest load for large asset sets, one file instead of thousands, and it is the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
* '''Con:''' you need a build step and the Unity tooling to produce it, and the output is not hand-editable.&lt;br /&gt;
&lt;br /&gt;
=== Quick comparison ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Approach !! Load cost !! Disk size !! Can carry !! Editable after build? !! Good for&lt;br /&gt;
|-&lt;br /&gt;
| Loose PNG || Converted every load || Small || Textures, sounds || Yes || Small mods, prototyping&lt;br /&gt;
|-&lt;br /&gt;
| DDS || None (pre-converted) || Large || Textures || Painful || Large texture sets&lt;br /&gt;
|-&lt;br /&gt;
| Asset bundle || None (pre-converted) || Smallest || Textures, meshes, shaders, fonts, audio || No (rebuild from source) || Large mods, anything non-texture&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
That disk-size column is worth some real numbers. Take one texture set of around 960 files and store it all three ways:&lt;br /&gt;
&lt;br /&gt;
* As loose PNGs: about 40 MB.&lt;br /&gt;
* Converted to DDS: about 230 MB. The conversion skips the runtime work but blows up the size on disk.&lt;br /&gt;
* Packed into a single asset bundle: about 28 MB.&lt;br /&gt;
&lt;br /&gt;
So DDS is the ''largest'' on disk, not the smallest. It trades space for skipping the load-time conversion. The bundle gets that same load-time win and still ends up the smallest of the three, because it is compressed into one file. (Numbers from a comparison shared in the [https://discord.com/channels/214523379766525963/632790371256238120/1405733585268375643 RimWorld modding Discord]; your exact figures will vary with the texture set and compression settings.)&lt;br /&gt;
&lt;br /&gt;
== The workflow, in plain terms ==&lt;br /&gt;
&lt;br /&gt;
Before any code or folder layout, here is what building a bundle actually looks like from a height:&lt;br /&gt;
&lt;br /&gt;
# You keep your real, editable source assets (PNGs, Unity materials, shader files, font files) in a Unity project.&lt;br /&gt;
# In that Unity project you tag each asset with a bundle name, then tell Unity to build the bundles.&lt;br /&gt;
# Unity spits out the bundle files.&lt;br /&gt;
# You ship those bundle files inside your RimWorld mod.&lt;br /&gt;
# At game load, RimWorld finds the bundle on its own and serves its contents through the normal content lookup (1.6 does the loading for you; textures and sounds resolve by their existing path, while shaders and fonts you still fetch by name in code).&lt;br /&gt;
&lt;br /&gt;
That is the loop. You edit in Unity, build, copy the output into your mod, and the game picks it up. The first time through it feels like a lot of ceremony for a texture. Once it is set up, adding a new asset is just &amp;quot;drop it in the Unity project, rebuild, copy.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
You do not have to do every step by hand. The next section covers that before the build details, so you can go find a tool first if you would rather not set this up yourself.&lt;br /&gt;
&lt;br /&gt;
== Before we get started: Do I actually have to do everything below? ==&lt;br /&gt;
&lt;br /&gt;
No. Setting up the Unity project, writing the build script, and juggling per-platform bundles is fiddly enough that several modders have written tools to do it for you. They wrap the whole thing, handle the multi-platform build, and spit out ready-to-ship bundles without you babysitting the Unity editor. If that sounds like what you want, you can stop here and go find one; the rest of this page is for when you want to understand what those tools are doing or build it yourself.&lt;br /&gt;
&lt;br /&gt;
This tutorial does not point at any specific one on purpose, since they come and go and get superseded. A search on Google or GitHub for RimWorld asset bundle tooling turns them up, and the modding Discord communities are usually the fastest place to find out which one people are actually using right now and which one is abandoned. Ask there before sinking an afternoon into a tool that got replaced six months ago.&lt;br /&gt;
&lt;br /&gt;
== Branch: the real build steps ==&lt;br /&gt;
&lt;br /&gt;
If you want the actual mechanics, here they are. First, install the right Unity editor. This matters more than anything else on this page: a bundle built on the wrong Unity version may silently fail to load. RimWorld 1.6 runs on '''Unity 2022.3.35f1''', so install exactly that version through the Unity Hub (under &amp;quot;Add&amp;quot; you can pick a specific archived version, or grab it from the Unity download archive). Build your bundles in 2022.3.35f1 and they line up with what the game expects. If RimWorld updates to a new Unity version down the line, you rebuild against the new one.&lt;br /&gt;
&lt;br /&gt;
=== Folder layout ===&lt;br /&gt;
&lt;br /&gt;
Inside your mod, bundles usually live somewhere like:&lt;br /&gt;
&lt;br /&gt;
 MyMod/&lt;br /&gt;
   About/&lt;br /&gt;
   Defs/&lt;br /&gt;
   Textures/&lt;br /&gt;
   AssetBundles/&lt;br /&gt;
     mymod_assets_win&lt;br /&gt;
     mymod_assets_mac&lt;br /&gt;
     mymod_assets_linux&lt;br /&gt;
&lt;br /&gt;
The bundle file itself has no extension. You name it whatever you tagged it in Unity, with the OS suffix on the end (see the deep dive for why the three files). There is no &amp;lt;code&amp;gt;.dll&amp;lt;/code&amp;gt; required to use a bundle: a pure XML-and-textures mod can ship a bundle and never write a line of C#, because the game loads it and serves its contents automatically.&lt;br /&gt;
&lt;br /&gt;
=== Tagging and building in Unity ===&lt;br /&gt;
&lt;br /&gt;
In the Unity project, select an asset, and at the bottom of the Inspector there is an AssetBundle dropdown. Assign a bundle name there. Everything sharing that name builds into the same bundle.&lt;br /&gt;
&lt;br /&gt;
Then you need a small editor script to actually trigger the build, because Unity does not expose bundle building in the default menus.&lt;br /&gt;
&lt;br /&gt;
The bare version is just one &amp;lt;code&amp;gt;BuildPipeline.BuildAssetBundles&amp;lt;/code&amp;gt; call. It works, but it ships your textures with whatever import settings they happened to have, which is usually uncompressed and wasteful. Controlling how each texture imports before it gets packed is what actually shrinks the disk and memory footprint, so the script below does two things: it walks your textures and sets sane import settings on each, then builds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
using UnityEditor;&lt;br /&gt;
using UnityEngine;&lt;br /&gt;
using System.IO;&lt;br /&gt;
&lt;br /&gt;
public static class BundleBuilder&lt;br /&gt;
{&lt;br /&gt;
    [MenuItem(&amp;quot;Assets/Build AssetBundles&amp;quot;)]&lt;br /&gt;
    static void Build()&lt;br /&gt;
    {&lt;br /&gt;
        // First pass: set good import settings on every texture in the project.&lt;br /&gt;
        foreach (string guid in AssetDatabase.FindAssets(&amp;quot;t:Texture2D&amp;quot;))&lt;br /&gt;
        {&lt;br /&gt;
            string assetPath = AssetDatabase.GUIDToAssetPath(guid);&lt;br /&gt;
            if (AssetImporter.GetAtPath(assetPath) is not TextureImporter tex) continue;&lt;br /&gt;
&lt;br /&gt;
            string lower = assetPath.ToLower();&lt;br /&gt;
            bool isTerrain = lower.Contains(&amp;quot;/terrain/&amp;quot;);&lt;br /&gt;
            bool isMask    = lower.Contains(&amp;quot;_mask&amp;quot;);&lt;br /&gt;
            bool isNormal  = lower.Contains(&amp;quot;_normal&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            // GUI type suits RimWorld's flat 2D art and avoids Unity generating sprite sub-assets.&lt;br /&gt;
            tex.textureType       = isNormal ? TextureImporterType.NormalMap : TextureImporterType.GUI;&lt;br /&gt;
            tex.alphaIsTransparency = true;&lt;br /&gt;
&lt;br /&gt;
            // Mask and normal maps hold raw data, not color, so they must stay linear (sRGB off).&lt;br /&gt;
            tex.sRGBTexture = !isMask &amp;amp;&amp;amp; !isNormal;&lt;br /&gt;
&lt;br /&gt;
            // Terrain tiles repeat across the ground; everything else should clamp at its edges.&lt;br /&gt;
            tex.wrapMode  = isTerrain ? TextureWrapMode.Repeat : TextureWrapMode.Clamp;&lt;br /&gt;
            tex.anisoLevel = isTerrain ? 8 : 1;&lt;br /&gt;
&lt;br /&gt;
            tex.filterMode    = FilterMode.Trilinear;&lt;br /&gt;
            tex.mipmapEnabled = true;&lt;br /&gt;
&lt;br /&gt;
            // BC7 high-quality compression: small on disk and in VRAM, good for hand-painted art.&lt;br /&gt;
            tex.SetPlatformTextureSettings(new TextureImporterPlatformSettings&lt;br /&gt;
            {&lt;br /&gt;
                name       = &amp;quot;Standalone&amp;quot;,&lt;br /&gt;
                overridden = true,&lt;br /&gt;
                format     = TextureImporterFormat.BC7,&lt;br /&gt;
                maxTextureSize = 4096,&lt;br /&gt;
                textureCompression = TextureImporterCompression.CompressedHQ&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            tex.SaveAndReimport();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Second pass: build the bundles.&lt;br /&gt;
        string output = &amp;quot;Assets/BuiltBundles&amp;quot;;&lt;br /&gt;
        Directory.CreateDirectory(output);&lt;br /&gt;
        BuildPipeline.BuildAssetBundles(&lt;br /&gt;
            output,&lt;br /&gt;
            BuildAssetBundleOptions.ChunkBasedCompression, // LZ4&lt;br /&gt;
            BuildTarget.StandaloneWindows64&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A few notes on what that import pass is doing:&lt;br /&gt;
&lt;br /&gt;
* '''BC7 + CompressedHQ''' is the compression that gets you the small disk and VRAM footprint. Without it the texture sits in memory uncompressed.&lt;br /&gt;
* '''sRGB off for masks and normal maps.''' A stuff-color mask or a normal map stores data, not a picture, so it must be read as linear. Leaving sRGB on silently corrupts it. The script keys off &amp;lt;code&amp;gt;_mask&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;_normal&amp;lt;/code&amp;gt; in the filename, so name your files accordingly.&lt;br /&gt;
* '''Terrain repeats, everything else clamps.''' Terrain tiles need &amp;lt;code&amp;gt;Repeat&amp;lt;/code&amp;gt; wrap so they tile seamlessly across the ground; a regular sprite wants &amp;lt;code&amp;gt;Clamp&amp;lt;/code&amp;gt; so its edges do not bleed. The script keys off a &amp;lt;code&amp;gt;/Terrain/&amp;lt;/code&amp;gt; folder in the path.&lt;br /&gt;
* '''Mipmaps on''' so the texture does not shimmer when the camera zooms out.&lt;br /&gt;
&lt;br /&gt;
This is a trimmed-down version of what dedicated bundle-building tools do; they also handle audio compression, font atlas generation, and per-bundle include/exclude rules. The texture settings above are the ones worth knowing by hand. Run the menu item, Unity reimports the textures and writes the bundles to the output folder, and you copy the ones you care about into your mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
=== Loading the bundle in your mod ===&lt;br /&gt;
&lt;br /&gt;
In 1.6 you do not load the bundle yourself at all, which catches people off guard since the older docs make it look involved. RimWorld scans every active mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder on startup, loads what it finds, and keeps the loaded bundles on the mod. You do not write &amp;lt;code&amp;gt;AssetBundle.LoadFromFile&amp;lt;/code&amp;gt;, you do not call &amp;lt;code&amp;gt;LoadAsset&amp;lt;/code&amp;gt;, you do not run anything in a static constructor.&lt;br /&gt;
&lt;br /&gt;
The game's normal content lookup also checks bundles for you. When you ask for a texture the usual way, with &amp;lt;code&amp;gt;ContentFinder&amp;lt;Texture2D&amp;gt;.Get(&amp;quot;Things/Item/MyThing&amp;quot;)&amp;lt;/code&amp;gt;, RimWorld first looks for a loose file at that path, and if it does not find one it looks inside your loaded bundles at the matching path. Same call, same path string, whether the texture is loose or bundled. The asset just has to live at the same path inside the bundle that the loose file would have used (so an asset packed at &amp;lt;code&amp;gt;Textures/Things/Item/MyThing&amp;lt;/code&amp;gt; resolves for the path above).&lt;br /&gt;
&lt;br /&gt;
The practical upshot: for textures and sounds you do not change a single line of your mod's code to switch from loose to bundled. You reference them by path like you always did. You only build a bundle, drop it in the folder, and the game finds the contents.&lt;br /&gt;
&lt;br /&gt;
The exception is shaders. The content lookup skips the loose-file step for shaders entirely (there is no loose-shader path), so a shader is only ever found inside a bundle. &amp;lt;code&amp;gt;ContentFinder&amp;lt;Shader&amp;gt;.Get(&amp;quot;MyShaderName&amp;quot;)&amp;lt;/code&amp;gt; works, but only because the shader is in a bundle for it to find.&lt;br /&gt;
&lt;br /&gt;
== Branch: the deep dive ==&lt;br /&gt;
&lt;br /&gt;
Most of this only comes up once you are shipping a shader or a font. A small texture-only mod can skip the whole section.&lt;br /&gt;
&lt;br /&gt;
=== Shaders and the cross-platform problem ===&lt;br /&gt;
&lt;br /&gt;
A compiled shader is platform-specific, which is why a lot of mods end up shipping multiple bundles. A shader built for DirectX (Windows) is not the same bytes as one built for Metal (Mac) or Vulkan/OpenGL (Linux). If you build one bundle on Windows and ship it, your shader works on Windows and quietly fails on Mac and Linux, usually showing up as bright magenta where the texture should be.&lt;br /&gt;
&lt;br /&gt;
The fix is to build a bundle per platform, and 1.6 makes this almost free because the game does the picking for you. Name your bundle files with the suffix the game looks for:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_win&amp;lt;/code&amp;gt; loads on Windows&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_mac&amp;lt;/code&amp;gt; loads on Mac&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_linux&amp;lt;/code&amp;gt; loads on Linux&lt;br /&gt;
&lt;br /&gt;
The game checks the running platform and loads only the matching one. A bundle with no suffix at all loads on every platform, which is fine for textures and meshes that do not care, but not safe for shaders. So you build the same assets three times in Unity with different &amp;lt;code&amp;gt;BuildTarget&amp;lt;/code&amp;gt; values (&amp;lt;code&amp;gt;StandaloneWindows64&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneOSX&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneLinux64&amp;lt;/code&amp;gt;), give each the right suffix, and ship all three. You write no platform-detection code yourself.&lt;br /&gt;
&lt;br /&gt;
If you have ever installed a mod that worked fine for everyone except the Mac and Linux players who saw pink everywhere, this is almost always why.&lt;br /&gt;
&lt;br /&gt;
=== Sounds ===&lt;br /&gt;
&lt;br /&gt;
Audio can go in a bundle as an &amp;lt;code&amp;gt;AudioClip&amp;lt;/code&amp;gt;, and for a lot of sounds it is fine and saves you the loose-file management. RimWorld can also load loose audio through its normal sound def system, so for ordinary sound effects you often do not need a bundle at all. Where bundles help is when you want the audio packed with everything else, or when you are loading clips directly from code rather than through a SoundDef.&lt;br /&gt;
&lt;br /&gt;
=== Fonts ===&lt;br /&gt;
&lt;br /&gt;
If you want a custom font in your UI, a bundle is the way. You import the font into Unity, build it into a bundle, load it as a &amp;lt;code&amp;gt;Font&amp;lt;/code&amp;gt;, and assign it where you draw text. There is no loose-font path in RimWorld at all, so here the bundle is not a speed-up, it is the only way in.&lt;br /&gt;
&lt;br /&gt;
=== Meshes ===&lt;br /&gt;
&lt;br /&gt;
Same story as fonts. Custom 3D meshes (not the usual flat sprites, actual geometry) come in through a bundle as a &amp;lt;code&amp;gt;Mesh&amp;lt;/code&amp;gt;. Most RimWorld mods never touch this, but if you are doing something with real geometry it is here.&lt;br /&gt;
&lt;br /&gt;
=== Compression and a couple of gotchas ===&lt;br /&gt;
&lt;br /&gt;
A few things worth knowing before you spend an afternoon confused:&lt;br /&gt;
&lt;br /&gt;
* '''Unity version mismatch.''' A bundle built on a different Unity version than the game uses may refuse to load, often with no clear error, the asset just comes back null. Build on Unity 2022.3.35f1 for RimWorld 1.6. This is the single most common reason a bundle that worked in the editor does nothing in-game.&lt;br /&gt;
* '''Bundle file compression.''' This is how the bundle file is packed on disk, separate from texture compression below. Bundles can be built uncompressed, LZ4, or LZMA. LZ4 is the usual sweet spot: small enough, and it does not stall on load the way LZMA can. &amp;lt;code&amp;gt;BuildAssetBundleOptions.ChunkBasedCompression&amp;lt;/code&amp;gt; gives you LZ4.&lt;br /&gt;
* '''In-memory texture compression and the white-outline bug.''' Loose textures are compressed in memory once loaded, and that compression is what produces the faint white halo people sometimes see around a sprite (the art program threw away color data in fully transparent pixels). A bundle lets you turn that in-memory compression off for its textures, which sidesteps the halo without the usual workaround of padding the outline. The [[Modding_Tutorials/Textures|Textures]] page documents the bug and the loose-texture fix.&lt;br /&gt;
* '''Shader stripping.''' Unity sometimes strips shader variants it thinks are unused during the build, and then your shader looks wrong at runtime because the variant you needed got cut. If a shader misbehaves only in the built bundle but works in the editor, this is a likely suspect.&lt;br /&gt;
* '''Color space.''' If your textures look washed out or too dark coming out of a bundle, check that the import settings and color space match what the game expects. This is the same class of problem that makes a hand-rolled DDS look wrong.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
* Loose PNG: simplest, but converted on every load and limited to textures and sounds.&lt;br /&gt;
* DDS: a texture pre-converted to GPU format, so it skips the load-time conversion. Same final look, faster load, but you have to make it right, and it lands much larger on disk than the source PNGs.&lt;br /&gt;
* Asset bundle: one pre-packed file that carries textures, meshes, shaders, fonts, and audio. The fastest option for big mods and the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
&lt;br /&gt;
If your mod is small, stay loose. If it is large or needs anything Unity-shaped beyond a texture, learn bundles, and on 1.6+ that is the encouraged path anyway since the engine does the loading and per-OS swapping for you. And if you are shipping a shader, build one bundle per platform with the right suffix, or your Mac and Linux players get magenta.&lt;br /&gt;
&lt;br /&gt;
[[Category:Modding]]&lt;br /&gt;
[[Category:Modding tutorials]]&lt;/div&gt;</summary>
		<author><name>CryptikLemur</name></author>
	</entry>
	<entry>
		<id>https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181159</id>
		<title>Modding Tutorials/Asset Bundles</title>
		<link rel="alternate" type="text/html" href="https://rimworldwiki.com/index.php?title=Modding_Tutorials/Asset_Bundles&amp;diff=181159"/>
		<updated>2026-06-24T19:30:14Z</updated>

		<summary type="html">&lt;p&gt;CryptikLemur: Adding new page on Asset Bundles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Asset Bundles}}&lt;br /&gt;
&lt;br /&gt;
{{BackToTutorials}}&lt;br /&gt;
&lt;br /&gt;
This tutorial explains what asset bundles are, why you might want them, and how they compare to the two other ways RimWorld can load custom art: loose textures and DDS files. It starts from zero, so you do not need to know anything about Unity going in. The later sections branch into the actual build process and then into the deeper stuff: shaders, sounds, fonts, and the things that go wrong.&lt;br /&gt;
&lt;br /&gt;
== What is an asset bundle? ==&lt;br /&gt;
&lt;br /&gt;
An asset bundle is a single file that holds a bunch of Unity assets already packed in the format the engine wants. RimWorld runs on Unity, and Unity has its own internal way of storing textures, meshes, shaders, audio, and so on. A loose PNG sitting in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder is not in that format yet, so the game has to convert it before it can use it.&lt;br /&gt;
&lt;br /&gt;
A bundle is that conversion done ahead of time, saved to disk, and handed to the engine in one piece. The rest of this page is mostly consequences of that.&lt;br /&gt;
&lt;br /&gt;
== Why would I care? ==&lt;br /&gt;
&lt;br /&gt;
Two reasons, mostly:&lt;br /&gt;
&lt;br /&gt;
'''Load time.''' When you ship loose PNGs, RimWorld converts every single one while the game boots (more on this in the next section). For a small mod you will never notice. For a mod with hundreds or thousands of textures, that conversion adds up, and it is part of why a heavily-modded load screen sits there for a while. A bundle skips the conversion because the work is already done.&lt;br /&gt;
&lt;br /&gt;
'''Everything that is not a texture.''' Loose files only really work for textures and sounds. If you want to ship a custom shader, a custom font, a custom mesh, or anything else Unity-shaped, a bundle is the normal way to get it into the game. There is no &amp;lt;code&amp;gt;Shaders/&amp;lt;/code&amp;gt; folder you can just drop a file into.&lt;br /&gt;
&lt;br /&gt;
If your mod is XML and a handful of textures, you probably do not need bundles at all. If your mod is large, or it needs a custom shader or font, you do.&lt;br /&gt;
&lt;br /&gt;
As of 1.6, bundles are the encouraged path for anything beyond a small loose-texture mod. The engine grew real first-class support for them: it loads them for you, swaps the right one per operating system, and serves their contents through the same content lookup you already use. Earlier versions made you wire all of that up by hand, which is why older tutorials look more involved than this one. On 1.6+ most of the friction is gone.&lt;br /&gt;
&lt;br /&gt;
== Loose textures vs DDS vs asset bundles ==&lt;br /&gt;
&lt;br /&gt;
All three of these get a texture onto the screen. They differ in '''when''' the work happens and '''what''' they can carry. Mix those two questions up and the whole topic gets confusing fast.&lt;br /&gt;
&lt;br /&gt;
=== Loose textures (PNG) ===&lt;br /&gt;
&lt;br /&gt;
You put a &amp;lt;code&amp;gt;.png&amp;lt;/code&amp;gt; in your mod's &amp;lt;code&amp;gt;Textures/&amp;lt;/code&amp;gt; folder and reference it by path. Simplest possible setup, and what almost every tutorial starts with. (The [[Modding_Tutorials/Textures|Textures]] page covers the path rules and texture conventions in full; this page assumes you have that down.)&lt;br /&gt;
&lt;br /&gt;
RimWorld does not render your PNG directly. On load it converts the PNG into a compressed GPU texture (the same family of formats DDS uses) and caches the result. So the PNG is a source file, not the thing the GPU actually draws. That conversion is cheap for one texture and not cheap for a thousand.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' dead simple, easy to edit, easy to diff, no tooling.&lt;br /&gt;
* '''Con:''' the game pays a conversion cost on load, and you cannot ship anything other than textures and sounds this way.&lt;br /&gt;
&lt;br /&gt;
=== DDS textures ===&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;code&amp;gt;.dds&amp;lt;/code&amp;gt; file is a texture that is ''already'' in a GPU-ready compressed format. If you drop a DDS next to where the PNG would go, RimWorld uses it directly and skips the conversion step entirely.&lt;br /&gt;
&lt;br /&gt;
So the loose-vs-DDS question is mostly about that conversion cost, not about how the final image looks. A correctly-made DDS and the game's own PNG conversion land in roughly the same place visually. What DDS buys you is a faster load and no first-load hitch, because the expensive part already happened on your machine when you made the file.&lt;br /&gt;
&lt;br /&gt;
The catch is you have to ''make'' the DDS, and if you make it wrong (wrong compression, no mipmaps, wrong color space) it can look worse than just letting the game convert the PNG. It is a real win for large texture sets, and a footgun for people who do not know what BC7 or mipmaps are.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' no runtime conversion, faster load, no first-load stutter for that texture.&lt;br /&gt;
* '''Con:''' you have to generate it correctly, harder to edit after the fact, much larger on disk than the source PNGs, and still textures-only.&lt;br /&gt;
&lt;br /&gt;
=== Asset bundles ===&lt;br /&gt;
&lt;br /&gt;
A bundle is the next step up. It is one file that can hold many textures (already in GPU format, like DDS, so same load-time win) ''plus'' meshes, shaders, fonts, and audio. Instead of hundreds of loose files converted one by one, the engine loads one bundle and everything inside is ready to go.&lt;br /&gt;
&lt;br /&gt;
The trade is that a bundle is opaque. You cannot open it in an image editor and tweak a pixel. You rebuild it from your source assets, which means you keep your editable PNGs and Unity sources somewhere and treat the bundle as a build output, the same way you treat a compiled DLL.&lt;br /&gt;
&lt;br /&gt;
* '''Pro:''' fastest load for large asset sets, one file instead of thousands, and it is the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
* '''Con:''' you need a build step and the Unity tooling to produce it, and the output is not hand-editable.&lt;br /&gt;
&lt;br /&gt;
=== Quick comparison ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Approach !! Load cost !! Disk size !! Can carry !! Editable after build? !! Good for&lt;br /&gt;
|-&lt;br /&gt;
| Loose PNG || Converted every load || Small || Textures, sounds || Yes || Small mods, prototyping&lt;br /&gt;
|-&lt;br /&gt;
| DDS || None (pre-converted) || Large || Textures || Painful || Large texture sets&lt;br /&gt;
|-&lt;br /&gt;
| Asset bundle || None (pre-converted) || Smallest || Textures, meshes, shaders, fonts, audio || No (rebuild from source) || Large mods, anything non-texture&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
That disk-size column is worth some real numbers. Take one texture set of around 960 files and store it all three ways:&lt;br /&gt;
&lt;br /&gt;
* As loose PNGs: about 40 MB.&lt;br /&gt;
* Converted to DDS: about 230 MB. The conversion skips the runtime work but blows up the size on disk.&lt;br /&gt;
* Packed into a single asset bundle: about 28 MB.&lt;br /&gt;
&lt;br /&gt;
So DDS is the ''largest'' on disk, not the smallest. It trades space for skipping the load-time conversion. The bundle gets that same load-time win and still ends up the smallest of the three, because it is compressed into one file. (Numbers from a comparison shared in the [https://discord.com/channels/214523379766525963/632790371256238120/1405733585268375643 RimWorld modding Discord]; your exact figures will vary with the texture set and compression settings.)&lt;br /&gt;
&lt;br /&gt;
== The workflow, in plain terms ==&lt;br /&gt;
&lt;br /&gt;
Before any code or folder layout, here is what building a bundle actually looks like from a height:&lt;br /&gt;
&lt;br /&gt;
# You keep your real, editable source assets (PNGs, Unity materials, shader files, font files) in a Unity project.&lt;br /&gt;
# In that Unity project you tag each asset with a bundle name, then tell Unity to build the bundles.&lt;br /&gt;
# Unity spits out the bundle files.&lt;br /&gt;
# You ship those bundle files inside your RimWorld mod.&lt;br /&gt;
# At game load, RimWorld finds the bundle on its own and serves its contents through the normal content lookup (1.6 does the loading for you; textures and sounds resolve by their existing path, while shaders and fonts you still fetch by name in code).&lt;br /&gt;
&lt;br /&gt;
That is the loop. You edit in Unity, build, copy the output into your mod, and the game picks it up. The first time through it feels like a lot of ceremony for a texture. Once it is set up, adding a new asset is just &amp;quot;drop it in the Unity project, rebuild, copy.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
You do not have to do every step by hand. The next section covers that before the build details, so you can go find a tool first if you would rather not set this up yourself.&lt;br /&gt;
&lt;br /&gt;
== Before we get started: Do I actually have to do everything below? ==&lt;br /&gt;
&lt;br /&gt;
No. Setting up the Unity project, writing the build script, and juggling per-platform bundles is fiddly enough that several modders have written tools to do it for you. They wrap the whole thing, handle the multi-platform build, and spit out ready-to-ship bundles without you babysitting the Unity editor. If that sounds like what you want, you can stop here and go find one; the rest of this page is for when you want to understand what those tools are doing or build it yourself.&lt;br /&gt;
&lt;br /&gt;
This tutorial does not point at any specific one on purpose, since they come and go and get superseded. A search on Google or GitHub for RimWorld asset bundle tooling turns them up, and the modding Discord communities are usually the fastest place to find out which one people are actually using right now and which one is abandoned. Ask there before sinking an afternoon into a tool that got replaced six months ago.&lt;br /&gt;
&lt;br /&gt;
== Branch: the real build steps ==&lt;br /&gt;
&lt;br /&gt;
If you want the actual mechanics, here they are. First, install the right Unity editor. This matters more than anything else on this page: a bundle built on the wrong Unity version may silently fail to load. RimWorld 1.6 runs on '''Unity 2022.3.35f1''', so install exactly that version through the Unity Hub (under &amp;quot;Add&amp;quot; you can pick a specific archived version, or grab it from the Unity download archive). Build your bundles in 2022.3.35f1 and they line up with what the game expects. If RimWorld updates to a new Unity version down the line, you rebuild against the new one.&lt;br /&gt;
&lt;br /&gt;
=== Folder layout ===&lt;br /&gt;
&lt;br /&gt;
Inside your mod, bundles usually live somewhere like:&lt;br /&gt;
&lt;br /&gt;
 MyMod/&lt;br /&gt;
   About/&lt;br /&gt;
   Defs/&lt;br /&gt;
   Textures/&lt;br /&gt;
   AssetBundles/&lt;br /&gt;
     mymod_assets_win&lt;br /&gt;
     mymod_assets_mac&lt;br /&gt;
     mymod_assets_linux&lt;br /&gt;
&lt;br /&gt;
The bundle file itself has no extension. You name it whatever you tagged it in Unity, with the OS suffix on the end (see the deep dive for why the three files). There is no &amp;lt;code&amp;gt;.dll&amp;lt;/code&amp;gt; required to use a bundle: a pure XML-and-textures mod can ship a bundle and never write a line of C#, because the game loads it and serves its contents automatically.&lt;br /&gt;
&lt;br /&gt;
=== Tagging and building in Unity ===&lt;br /&gt;
&lt;br /&gt;
In the Unity project, select an asset, and at the bottom of the Inspector there is an AssetBundle dropdown. Assign a bundle name there. Everything sharing that name builds into the same bundle.&lt;br /&gt;
&lt;br /&gt;
Then you need a small editor script to actually trigger the build, because Unity does not expose bundle building in the default menus.&lt;br /&gt;
&lt;br /&gt;
The bare version is just one &amp;lt;code&amp;gt;BuildPipeline.BuildAssetBundles&amp;lt;/code&amp;gt; call. It works, but it ships your textures with whatever import settings they happened to have, which is usually uncompressed and wasteful. Controlling how each texture imports before it gets packed is what actually shrinks the disk and memory footprint, so the script below does two things: it walks your textures and sets sane import settings on each, then builds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
using UnityEditor;&lt;br /&gt;
using UnityEngine;&lt;br /&gt;
using System.IO;&lt;br /&gt;
&lt;br /&gt;
public static class BundleBuilder&lt;br /&gt;
{&lt;br /&gt;
    [MenuItem(&amp;quot;Assets/Build AssetBundles&amp;quot;)]&lt;br /&gt;
    static void Build()&lt;br /&gt;
    {&lt;br /&gt;
        // First pass: set good import settings on every texture in the project.&lt;br /&gt;
        foreach (string guid in AssetDatabase.FindAssets(&amp;quot;t:Texture2D&amp;quot;))&lt;br /&gt;
        {&lt;br /&gt;
            string assetPath = AssetDatabase.GUIDToAssetPath(guid);&lt;br /&gt;
            if (AssetImporter.GetAtPath(assetPath) is not TextureImporter tex) continue;&lt;br /&gt;
&lt;br /&gt;
            string lower = assetPath.ToLower();&lt;br /&gt;
            bool isTerrain = lower.Contains(&amp;quot;/terrain/&amp;quot;);&lt;br /&gt;
            bool isMask    = lower.Contains(&amp;quot;_mask&amp;quot;);&lt;br /&gt;
            bool isNormal  = lower.Contains(&amp;quot;_normal&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            // GUI type suits RimWorld's flat 2D art and avoids Unity generating sprite sub-assets.&lt;br /&gt;
            tex.textureType       = isNormal ? TextureImporterType.NormalMap : TextureImporterType.GUI;&lt;br /&gt;
            tex.alphaIsTransparency = true;&lt;br /&gt;
&lt;br /&gt;
            // Mask and normal maps hold raw data, not color, so they must stay linear (sRGB off).&lt;br /&gt;
            tex.sRGBTexture = !isMask &amp;amp;&amp;amp; !isNormal;&lt;br /&gt;
&lt;br /&gt;
            // Terrain tiles repeat across the ground; everything else should clamp at its edges.&lt;br /&gt;
            tex.wrapMode  = isTerrain ? TextureWrapMode.Repeat : TextureWrapMode.Clamp;&lt;br /&gt;
            tex.anisoLevel = isTerrain ? 8 : 1;&lt;br /&gt;
&lt;br /&gt;
            tex.filterMode    = FilterMode.Trilinear;&lt;br /&gt;
            tex.mipmapEnabled = true;&lt;br /&gt;
&lt;br /&gt;
            // BC7 high-quality compression: small on disk and in VRAM, good for hand-painted art.&lt;br /&gt;
            tex.SetPlatformTextureSettings(new TextureImporterPlatformSettings&lt;br /&gt;
            {&lt;br /&gt;
                name       = &amp;quot;Standalone&amp;quot;,&lt;br /&gt;
                overridden = true,&lt;br /&gt;
                format     = TextureImporterFormat.BC7,&lt;br /&gt;
                maxTextureSize = 4096,&lt;br /&gt;
                textureCompression = TextureImporterCompression.CompressedHQ&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            tex.SaveAndReimport();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Second pass: build the bundles.&lt;br /&gt;
        string output = &amp;quot;Assets/BuiltBundles&amp;quot;;&lt;br /&gt;
        Directory.CreateDirectory(output);&lt;br /&gt;
        BuildPipeline.BuildAssetBundles(&lt;br /&gt;
            output,&lt;br /&gt;
            BuildAssetBundleOptions.ChunkBasedCompression, // LZ4&lt;br /&gt;
            BuildTarget.StandaloneWindows64&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A few notes on what that import pass is doing:&lt;br /&gt;
&lt;br /&gt;
* '''BC7 + CompressedHQ''' is the compression that gets you the small disk and VRAM footprint. Without it the texture sits in memory uncompressed.&lt;br /&gt;
* '''sRGB off for masks and normal maps.''' A stuff-color mask or a normal map stores data, not a picture, so it must be read as linear. Leaving sRGB on silently corrupts it. The script keys off &amp;lt;code&amp;gt;_mask&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;_normal&amp;lt;/code&amp;gt; in the filename, so name your files accordingly.&lt;br /&gt;
* '''Terrain repeats, everything else clamps.''' Terrain tiles need &amp;lt;code&amp;gt;Repeat&amp;lt;/code&amp;gt; wrap so they tile seamlessly across the ground; a regular sprite wants &amp;lt;code&amp;gt;Clamp&amp;lt;/code&amp;gt; so its edges do not bleed. The script keys off a &amp;lt;code&amp;gt;/Terrain/&amp;lt;/code&amp;gt; folder in the path.&lt;br /&gt;
* '''Mipmaps on''' so the texture does not shimmer when the camera zooms out.&lt;br /&gt;
&lt;br /&gt;
This is a trimmed-down version of what dedicated bundle-building tools do; they also handle audio compression, font atlas generation, and per-bundle include/exclude rules. The texture settings above are the ones worth knowing by hand. Run the menu item, Unity reimports the textures and writes the bundles to the output folder, and you copy the ones you care about into your mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
=== Loading the bundle in your mod ===&lt;br /&gt;
&lt;br /&gt;
In 1.6 you do not load the bundle yourself at all, which catches people off guard since the older docs make it look involved. RimWorld scans every active mod's &amp;lt;code&amp;gt;AssetBundles/&amp;lt;/code&amp;gt; folder on startup, loads what it finds, and keeps the loaded bundles on the mod. You do not write &amp;lt;code&amp;gt;AssetBundle.LoadFromFile&amp;lt;/code&amp;gt;, you do not call &amp;lt;code&amp;gt;LoadAsset&amp;lt;/code&amp;gt;, you do not run anything in a static constructor.&lt;br /&gt;
&lt;br /&gt;
The game's normal content lookup also checks bundles for you. When you ask for a texture the usual way, with &amp;lt;code&amp;gt;ContentFinder&amp;lt;Texture2D&amp;gt;.Get(&amp;quot;Things/Item/MyThing&amp;quot;)&amp;lt;/code&amp;gt;, RimWorld first looks for a loose file at that path, and if it does not find one it looks inside your loaded bundles at the matching path. Same call, same path string, whether the texture is loose or bundled. The asset just has to live at the same path inside the bundle that the loose file would have used (so an asset packed at &amp;lt;code&amp;gt;Textures/Things/Item/MyThing&amp;lt;/code&amp;gt; resolves for the path above).&lt;br /&gt;
&lt;br /&gt;
The practical upshot: for textures and sounds you do not change a single line of your mod's code to switch from loose to bundled. You reference them by path like you always did. You only build a bundle, drop it in the folder, and the game finds the contents.&lt;br /&gt;
&lt;br /&gt;
The exception is shaders. The content lookup skips the loose-file step for shaders entirely (there is no loose-shader path), so a shader is only ever found inside a bundle. &amp;lt;code&amp;gt;ContentFinder&amp;lt;Shader&amp;gt;.Get(&amp;quot;MyShaderName&amp;quot;)&amp;lt;/code&amp;gt; works, but only because the shader is in a bundle for it to find.&lt;br /&gt;
&lt;br /&gt;
== Branch: the deep dive ==&lt;br /&gt;
&lt;br /&gt;
Most of this only comes up once you are shipping a shader or a font. A small texture-only mod can skip the whole section.&lt;br /&gt;
&lt;br /&gt;
=== Shaders and the cross-platform problem ===&lt;br /&gt;
&lt;br /&gt;
A compiled shader is platform-specific, which is why a lot of mods end up shipping multiple bundles. A shader built for DirectX (Windows) is not the same bytes as one built for Metal (Mac) or Vulkan/OpenGL (Linux). If you build one bundle on Windows and ship it, your shader works on Windows and quietly fails on Mac and Linux, usually showing up as bright magenta where the texture should be.&lt;br /&gt;
&lt;br /&gt;
The fix is to build a bundle per platform, and 1.6 makes this almost free because the game does the picking for you. Name your bundle files with the suffix the game looks for:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_win&amp;lt;/code&amp;gt; loads on Windows&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_mac&amp;lt;/code&amp;gt; loads on Mac&lt;br /&gt;
* &amp;lt;code&amp;gt;mymod_assets_linux&amp;lt;/code&amp;gt; loads on Linux&lt;br /&gt;
&lt;br /&gt;
The game checks the running platform and loads only the matching one. A bundle with no suffix at all loads on every platform, which is fine for textures and meshes that do not care, but not safe for shaders. So you build the same assets three times in Unity with different &amp;lt;code&amp;gt;BuildTarget&amp;lt;/code&amp;gt; values (&amp;lt;code&amp;gt;StandaloneWindows64&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneOSX&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;StandaloneLinux64&amp;lt;/code&amp;gt;), give each the right suffix, and ship all three. You write no platform-detection code yourself.&lt;br /&gt;
&lt;br /&gt;
If you have ever installed a mod that worked fine for everyone except the Mac and Linux players who saw pink everywhere, this is almost always why.&lt;br /&gt;
&lt;br /&gt;
=== Sounds ===&lt;br /&gt;
&lt;br /&gt;
Audio can go in a bundle as an &amp;lt;code&amp;gt;AudioClip&amp;lt;/code&amp;gt;, and for a lot of sounds it is fine and saves you the loose-file management. RimWorld can also load loose audio through its normal sound def system, so for ordinary sound effects you often do not need a bundle at all. Where bundles help is when you want the audio packed with everything else, or when you are loading clips directly from code rather than through a SoundDef.&lt;br /&gt;
&lt;br /&gt;
=== Fonts ===&lt;br /&gt;
&lt;br /&gt;
If you want a custom font in your UI, a bundle is the way. You import the font into Unity, build it into a bundle, load it as a &amp;lt;code&amp;gt;Font&amp;lt;/code&amp;gt;, and assign it where you draw text. There is no loose-font path in RimWorld at all, so here the bundle is not a speed-up, it is the only way in.&lt;br /&gt;
&lt;br /&gt;
=== Meshes ===&lt;br /&gt;
&lt;br /&gt;
Same story as fonts. Custom 3D meshes (not the usual flat sprites, actual geometry) come in through a bundle as a &amp;lt;code&amp;gt;Mesh&amp;lt;/code&amp;gt;. Most RimWorld mods never touch this, but if you are doing something with real geometry it is here.&lt;br /&gt;
&lt;br /&gt;
=== Compression and a couple of gotchas ===&lt;br /&gt;
&lt;br /&gt;
A few things worth knowing before you spend an afternoon confused:&lt;br /&gt;
&lt;br /&gt;
* '''Unity version mismatch.''' A bundle built on a different Unity version than the game uses may refuse to load, often with no clear error, the asset just comes back null. Build on Unity 2022.3.35f1 for RimWorld 1.6. This is the single most common reason a bundle that worked in the editor does nothing in-game.&lt;br /&gt;
* '''Bundle file compression.''' This is how the bundle file is packed on disk, separate from texture compression below. Bundles can be built uncompressed, LZ4, or LZMA. LZ4 is the usual sweet spot: small enough, and it does not stall on load the way LZMA can. &amp;lt;code&amp;gt;BuildAssetBundleOptions.ChunkBasedCompression&amp;lt;/code&amp;gt; gives you LZ4.&lt;br /&gt;
* '''In-memory texture compression and the white-outline bug.''' Loose textures are compressed in memory once loaded, and that compression is what produces the faint white halo people sometimes see around a sprite (the art program threw away color data in fully transparent pixels). A bundle lets you turn that in-memory compression off for its textures, which sidesteps the halo without the usual workaround of padding the outline. The [[Modding_Tutorials/Textures|Textures]] page documents the bug and the loose-texture fix.&lt;br /&gt;
* '''Shader stripping.''' Unity sometimes strips shader variants it thinks are unused during the build, and then your shader looks wrong at runtime because the variant you needed got cut. If a shader misbehaves only in the built bundle but works in the editor, this is a likely suspect.&lt;br /&gt;
* '''Color space.''' If your textures look washed out or too dark coming out of a bundle, check that the import settings and color space match what the game expects. This is the same class of problem that makes a hand-rolled DDS look wrong.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
* Loose PNG: simplest, but converted on every load and limited to textures and sounds.&lt;br /&gt;
* DDS: a texture pre-converted to GPU format, so it skips the load-time conversion. Same final look, faster load, but you have to make it right.&lt;br /&gt;
* Asset bundle: one pre-packed file that carries textures, meshes, shaders, fonts, and audio. The fastest option for big mods and the only option for shaders, fonts, and custom meshes.&lt;br /&gt;
&lt;br /&gt;
If your mod is small, stay loose. If it is large or needs anything Unity-shaped beyond a texture, learn bundles, and on 1.6+ that is the encouraged path anyway since the engine does the loading and per-OS swapping for you. And if you are shipping a shader, build one bundle per platform with the right suffix, or your Mac and Linux players get magenta.&lt;br /&gt;
&lt;br /&gt;
[[Category:Modding]]&lt;br /&gt;
[[Category:Modding tutorials]]&lt;/div&gt;</summary>
		<author><name>CryptikLemur</name></author>
	</entry>
</feed>