-
-
Notifications
You must be signed in to change notification settings - Fork 2
Assets
English | 日本語
The Assets library is DragNWash.ModFramework.Assets.dll, in the namespace DragNWash.ModFramework.Assets. It gives you fonts for text the game's own fonts can't show, and it loads textures and asset bundles in a way that doesn't crash Direct3D 12. It also has the asset tool, which lets you see what's loaded and replace the game's textures with your own.
[BepInDependency(GameFonts.Guid, BepInDependency.DependencyFlags.HardDependency)]Drag'n Wash runs on Unity 6000.3. There, creating or uploading a texture or font atlas at the wrong moment on Direct3D 12 (the default on Windows) can crash the game in D3D12ScratchAllocator (Unity issue UUM-140564). So:
-
Load textures, bundles and fonts from your plugin's
Awake, and keep the returned objects. - Check
GameInfo.IsDirect3D12orGameFonts.RuntimeUploadsAreSafebefore doing anything later on. Vulkan (Steam Deck) and Direct3D 11 handle runtime uploads fine. - If a player still crashes, the workaround is
-force-d3d11in the Steam launch options.
private Texture2D _sign;
private AssetBundle _bundle;
private void Awake()
{
string dir = Path.GetDirectoryName(Info.Location);
_sign = GameAssets.LoadTexture(Path.Combine(dir, "sign.png"));
_bundle = GameAssets.LoadBundle(Path.Combine(dir, "mymod.bundle"));
}| Member | What it does |
|---|---|
Texture2D LoadTexture(string path) |
Loads a PNG or JPG, or returns the texture already loaded from that file. Null when missing or not an image |
AssetBundle LoadBundle(string path) |
Opens a bundle, or returns the one already opened from that file, so two mods shipping the same bundle don't fail. Null when missing |
Both log a warning if you call them late on a renderer where that's risky.
Experimental. This came in with the Assets library 1.1.0 (released 2026-09-17).
The framework gives you tools for the game's assets, and what people make with them is their own responsibility, the same way it works with REFramework and similar tools. Nothing here ships the game's assets. The game's data, unchanged, never goes into a repository or a release (the framework's CI enforces that for its own), and anything made by hand or changed follows the content policy (Playing well with others, rule 7).
AssetCatalog.Textures(), Materials(), Meshes() and Shaders() list what's loaded right now, with name, size, format, whether the CPU can read it, and how many materials and sprites use it. They walk every loaded object of that kind, so call them when a button is pressed, not every frame.
With the Tool window installed, the Assets tab (F1, with Developer tools on) shows the loaded textures and the replacements mods shipped, on screen. See The Assets tab below.
In the Console you have assets textures [filter], assets replacements, assets apply and assets reload.
Put a PNG in your mod's folder, named after the game texture it replaces:
BepInEx/plugins/<YourMod>/assets/textures/<texture name>.png
The texture's name is whatever the Assets tab lists. At startup (the moment texture uploads are safe on Direct3D 12) the library reads every such file, and when each scene loads, it puts the file into every material property and sprite that used the original. A sprite keeps its rect, pivot and pixels-per-unit, so give the PNG the original's size, or the rect gets scaled with the image. AssetReplacements.ApplyNow() does the same again for materials or sprites the game has created since.
If two mods replace the same texture, the mod whose folder sorts last wins, and both are named in the log and in the Assets tab. Nothing gets overridden silently.
Not done yet: meshes, shaders, and targeting by anything other than the name. Skinned meshes (characters) are out of scope for now.
Reload files in the Assets tab (or assets reload) reads every replacement PNG again, swaps in the ones whose content changed, and applies them. It only reads the files that were there when the game started, so a PNG you add later is read at the next start (the tab tells you so, 1.5.0). Apply replacements only re-points materials and sprites at textures that are already loaded. If you set [Reload] WatchFiles = true in the library's config (Watch texture files on the Mods screen, an advanced setting), a PNG that changes on disk reloads by itself half a second after the last write (never on Direct3D 12). Both need Developer tools on.
A reload uploads textures while the game runs, and on Direct3D 12 that can crash it. A native crash can't be caught, so the library writes a marker file (BepInEx/config/<Assets GUID>.reload-in-progress) before uploading and removes it afterwards. If the marker is still there at the next start, the last reload took the game down. The log and the Mods screen say so, [Reload] AllowReload is switched off until you turn it back on, and after two such crashes on Direct3D 12 the button stays off. If you work with -force-d3d11 in the game's launch options, you avoid all of this.
Failures the library can see are handled file by file and never stop the rest. A PNG that isn't an image, or that an editor is still saving (it's retried three times), keeps its previous texture and shows in red under Replacements in the Assets tab, with the reason.
(1.5.0)
With the Tool window installed and Developer tools on, press F1 and open Assets. The top row switches between Textures (1,234) and Replacements (4) like tabs, with how many each has, and the one showing is marked. When some replacements need a look, a yellow "2 to check" sits beside them. The filter takes the rest of the row. It matches texture names, and under Replacements the mod's name too. The row below has List again, Apply replacements and Reload files, and pointing at any of them explains it on the hint line. In a narrow window both rows wrap, so nothing ends up off the edge.
Under the buttons is a status line that says what the last button did, or with a filter, how many rows show ("Showing 12 of 1,234 textures."). Only a warning stays in sight there. On Direct3D 12 it's "Direct3D 12: Reload files can crash the game. Apply replacements is always safe." When Reload files is off, it says why and how to have it back, for example "Turn AllowReload back on in Options > Mods > Drag'n Wash ModFramework: Assets to try again." On Direct3D 11 with reloading on, there's no line at all.
Textures. The tab lists the loaded textures by itself the first time you open it, after a moment of "Listing textures...", and again after Apply replacements or Reload files. If a scene loads after that, a yellow band says "The scene changed since this list was made." with a List again button. Each row has the name, the size and format, how many materials and sprites use it, a mark, and Inspect when the Inspector is installed. The mark tells two rows of the same name apart. A mod's replacement says from SignPack (the mod's name), and the game's texture it stands in for says original, replaced and is dimmed. A narrow list leaves the material and sprite counts out, and the size too when the name would get too little room. Click a texture's name to see a preview: the texture as it is on the GPU, scaled to fit, over a checked background, with its size, format and users. It sits beside the list, or above it in a narrow window.
Replacements. There's one row per file, with the texture's name, the mod (and the language, for a picture per language), and how it's doing:
| It says | Means |
|---|---|
| In 12 places | Fine. It's in use in that many materials and sprites. |
| Not used yet (yellow) | It hasn't gone in anywhere. The hint line says whether a texture of that name is loaded at all, so a typo in the file name shows up. |
| Not used: CleanSponges wins (yellow) | Another mod's file of the same name is the one used. The mod that lost gets a row of its own, so filtering by its name finds it. |
| Off (dim) | A language picture its mod has switched off. |
| Not reloaded: ... (red) | The file couldn't be read again, so the picture from before stays. The reason follows. |
Pointing at a row explains it on the hint line, along with anything cut off. A narrow window leaves the mod column out. When a new language's pictures are waiting for a restart on Direct3D 12, a band above the list says so, for example Pictures for "ja" apply after a restart (Direct3D 12).
What the buttons say. Results come in plain words. Apply replacements says "Everything was already in place." or "Put replacements into 5 more places.". Reload files says "Nothing changed on disk.", "Reloaded 2 files.", or how many had a problem, and then shows Replacements so you can see which. If there are PNGs that weren't there when the game started, a notice names them and says they're read when the game starts, so restart to use them. When a watched file reloads by itself ([Reload] WatchFiles, Direct3D 11), a notice says so, like "shop_sign.png changed on disk and was reloaded.", and the list is made again.
Both lists scroll with the gamepad stick and the d-pad, so on the Steam Deck you don't need the trackpad or the touch screen. The preview's Close and the rows' Inspect are bigger and easier to hit.
If you're making a tab of your own, the row of buttons that wraps in a narrow window is ToolWindow.FlowButton (Tool window).
Experimental. This came in with framework 1.4.0 and may change or go away in a later version.
From Assets 1.2.0, a mod can ship pictures that only apply in one language, such as a sign translated into Japanese. Drag'n Wash Localization uses this for its translated pictures.
private void Awake()
{
GameFonts.SetLanguage("ja");
string root = Path.Combine(Path.GetDirectoryName(Info.Location), "pictures");
// pictures/<language>/textures/<texture name>.png
AssetReplacements.AddLanguageFolder(MyMod.Guid, root, "textures");
}-
AssetReplacements.AddLanguageFolder(guid, root, subfolder)takes<root>/<language>/<subfolder>/*.png. A picture only applies whileGameFonts.Languageis that language, and it wins over a plain replacement of the same texture (both are named in the log). Call it fromAwake, afterGameFonts.SetLanguage. - Only the language in use is loaded. When the language changes, the previous pictures are taken back and the new ones are loaded. On Direct3D 12 the new ones wait for a restart, and
AssetReplacements.PendingLanguagenames the language that will apply then. - If a texture has no picture in the language, it falls back to the languages listed in that language's
fallback.txt(in<root>/<language>/<subfolder>/, one language per line, in order, and#starts a comment), then to a plain replacement, then to the game's own. A picture that can't be loaded falls back the same way. -
AssetReplacements.SetLanguageFoldersEnabled(guid, on)switches a mod's pictures off and on.AssetReplacements.Changedis raised after that, and after a language change. -
TextureReplacement.Languagenames the language a picture came from, and the Assets tab shows it. - Replacements can be taken back, because the library remembers what each material property and each sprite user held before.
The same version also adds read operations to the Operations registry: assets.textures.list, assets.materials.list, assets.meshes.list (with a name filter), assets.replacements.list (which game texture, from which mod, for which language) and assets.fonts.language.
This isn't built yet. The plan is that every kind with a plain form can be written to a file, changed, and brought back from a mod. What may go into a mod's release follows the content policy: made by hand or changed, never the game's data unchanged.
| Kind | Export | Import (a mod ships it) |
|---|---|---|
| Texture, Sprite | PNG, read back from the GPU, so the game's compressed textures work too | PNG, as today (assets/textures/) |
| Mesh (not skinned) | OBJ | OBJ, replacing the mesh of that name (assets/meshes/) |
| Material | JSON: the shader's property values and keywords | JSON with only the values to change (assets/materials/) |
| Shader | JSON: its properties and keywords (not the compiled shader) | none |
| AudioClip | WAV, for clips whose samples can be read (not streamed ones) | WAV, replacing the clip of that name (assets/audio/) |
| ScriptableObject and other serializable objects | JSON of their serialized fields | JSON with only the fields to change (assets/data/) |
| Text and dialogue | Drag'n Wash Localization's exports | its translation packs |
Exports would go to BepInEx/exports/<game build>/<kind>/ with a NOTICE.txt, from an Export button and an export <kind> <name> console command, for developer tools only. Imports would be read at startup and applied like textures, and an export left untouched would be refused in a mod's zip. Animation clips, fonts, compiled shaders and skinned meshes are out of scope.
To change values of components and materials without code, see Overrides and the Inspector's Export as overrides (Inspector). Both are new in 1.4.0 and experimental.
// Nothing to call for replacements: ship the files. To see what is there:
foreach (TextureInfo t in AssetCatalog.Textures())
if (t.MaterialUsers > 0) Log($"{t.Name} {t.Width}x{t.Height} {t.Format}");
// After creating materials or sprites of your own from game textures:
AssetReplacements.ApplyNow();The game's fonts cover Latin text. For Japanese, Chinese, Korean, Hebrew and other scripts, the library loads system font faces and adds them to TextMeshPro's fallback fonts.
private void Awake()
{
// Every text the mod may show in that language, prepared at startup.
GameFonts.Prepare("ja", myJapaneseTexts);
GameFonts.Prepare("zh-Hans", myChineseTexts);
// Order the fallback chain for the language being shown now.
GameFonts.SetLanguage("ja");
}| Member | What it does |
|---|---|
void Prepare(string language, IEnumerable<string> texts) |
Loads a face for each script the texts need and rasterizes their characters now. Call from Awake or Update. Characters that are already prepared cost nothing |
void SetLanguage(string language) |
Puts that language's faces first (so Chinese text uses a Chinese face, not the Japanese one). Fallbacks added by others stay after these. From Assets 1.1.1 the language is also noted in the crash reports' session record, so the crash report window speaks it |
string Language |
The language last set |
event Action CharactersPrepared |
Raised after new characters or faces were prepared |
string PreparedCharacters |
Everything prepared so far, for drawing the same text with another font (see Tool window) |
int CountUnprepared(IEnumerable<string> texts) |
How many distinct non-ASCII characters were never prepared |
void AddFontFolder(string folder) |
A folder of .ttf, .otf or .ttc files searched before the system's. The library's own fonts folder is always searched |
bool RuntimeUploadsAreSafe |
False on Direct3D 12 |
Language codes are locale codes such as ja, zh-Hans, zh-Hant, ko, he.
The library's Font atlas point size setting (on the Mods screen, under Show advanced settings) sets how sharp fallback glyphs look. It takes effect after a restart.
- Add to
TMP_Settings.fallbackFontAssetsyourself; the library orders that list for everyone. - Load textures, fonts or bundles lazily "when first needed" on Direct3D 12.
Players
Mod authors
- Getting started
- Playing well with others (the guide)
- Going online
- Installer
- Mod reload
- Overrides (no code)
- Graphs (no code, makes things happen)
Tools (F1, developer tools)
- Inspector
- Console
- Code graph
- Bridge (AI clients, MCP)
API
日本語
- ホーム
- プレイヤー向け · ランチャー · FAQ · クラッシュレポート
- はじめての Mod · ほかの Mod と一緒に動かす · 外と通信する Mod · インストーラー · Mod の再読み込み · Overrides · Graphs
- Inspector · Console · コードのグラフ · Bridge
- 中核 API · 操作の登録簿 · Text · Dialogue · Tool window · Assets · Flags and saves · GameEvents · SettingMeta
Links
- Repository
- Releases
- Changelog
- Design records: DESIGN · ROADMAP · CONTENT_POLICY