<?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-06-28T02:47:53Z</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=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>