Assets - tModLoader/tModLoader GitHub Wiki
In tModLoader, we use the Asset
class to work with assets such as textures (Texture2D
), shaders (Effect
), fonts (DynamicSpriteFont
), and sounds (SoundEffect
). The Asset
and AssetRepository
classes provide on-demand (asynchronous) loading and resource pack support.
This guide shows how to work with the asset system for optimal performance and compatibility.
This section is a quick overview of various best practices. See the later sections for more details on each of these topics.
You should always be working with the Asset
class directly up until the contained data needs to be used. For example, instead of storing a Texture2D sprite
and calling spriteBatch.Draw(sprite)
, store an Asset<Texture2D> sprite
and call spriteBatch.Draw(sprite.Value)
.
tModLoader will request and load textures for most modded content during mod loading. This preloading reduces the need to request textures on demand at the cost of loading time and RAM utilization.
- These assets are usually accessible in arrays found in the
Terraria.GameContent
static classes, such asTextureAssets
, if needed. - If you have extra textures, such as special effects for an NPC or Projectile, you can request them in
SetStaticDefaults
or one of the other Load hooks. - Vanilla doesn't preload all textures. For Item, NPC, Projectile, Background, and some other types of content unloaded Assets are populated into the fields of the
TextureAssets
class. Using these textures require first calling the respectiveMain.instance.LoadX
method to ensure that they are loaded. Vanilla textures without a correspondingMain.instance.LoadX
are most likely preloaded.
After PostSetupContent
, tModLoader will make sure all requested assets for the mod are fully loaded, so there is no need to .Wait()
for them during gameplay.
- This also means there is no need to use
AssetRequestMode.ImmediateLoad
when requesting textures during loading. Doing so will only slow down the load process dramatically.
If you don't want to preload extra assets, either because it's inconvenient or because you'd like to save RAM or load time, you can Request
them on demand at any time during the game.
- It is still recommended to use
AssetRequestMode.AsyncLoad
when requesting assets in-game to avoid reducing frame-rate.
You should strive to only request an asset once and then store the reference in a field for usage. It is more efficient to access the field than it is to call the Request
method. This can be done during mod loading or on demand via a null check. For singleton classes, storing the asset in a instance field is sufficient. For classes with multiple instances such as ModItem
, ModProjectile
, and ModNPC
, the asset will need to be stored in a static field and requested during Load
. There is no need to set the reference to null
in Unload
or manually Dispose
of the Asset
.
Loading assets with AssetRequestMode.ImmediateLoad
(or calling .Wait()
) is inefficient and unnecessary in most situations. Read the sections below to learn when it might be needed.
The ModContent.Request
method can be used to request any asset from any mod or Terraria, but there are other options that might slightly reduce code repetition. When using the Request
method, the Type
of the asset is passed in as a generic argument. For example, ModContent.Request<Texture2D>(pathHere)
requests a texture (Texture2D
) with the path specified. ModContent.Request<Effect>(pathHere)
would do the same but for a shader (Effect
) asset.
To load an asset contained in the current mod, simply call the Assets.Request
method on the Mod
class. Any ModType
class will have access to the Mod
instance via the Mod
property, so Mod.Assets.Request
would be written. Other classes not inheriting from ModType
can use ModContent.GetInstance<ModClass>()
to access the Mod
instance. Alternatively, the ModContent.Request
method can be used if the modder provides the ModName
as part of the asset path.
Requesting an asset during mod loading, such as in a ModType.Load
or SetStaticDefaults
method, is a good idea as it will cause errors to be thrown right away rather than during gameplay. This allows the mod maker to fix typos and other asset related errors more quickly.
public class ExampleHookProjectile : ModProjectile {
private static Asset<Texture2D> chainTexture;
public override void Load() {
// Option 1: Load the asset directly from the Mod class. Mod name is NOT part of the asset path.
chainTexture = Mod.Assets.Request<Texture2D>("Content/Items/Tools/ExampleHookChain");
// Option 2: Load the asset directly from the Mod class retrieved via ModContent.GetInstance. ModName is NOT part of the asset path.
chainTexture = ModContent.GetInstance<ExampleMod>().Assets.Request<Texture2D>("Content/Items/Tools/ExampleHookChain");
// Option 3: Load the asset using ModContent.Request. ModName must be at the start of the asset path.
chainTexture = ModContent.Request<Texture2D>("ExampleMod/Content/Items/Tools/ExampleHookChain");
}
}
Loading assets from other mods or Terraria is the same process as loading from your own mod, it just requires retrieving the Mod instance or adding the mod name to ModContent.Request
calls. It is usually simplest to just type out the full path for ModContent.Request
rather than deal with accessing the Mod
classes of other mods. For Terraria assets, Terraria/
will be added to the path. Note that Terraria assets follow their path from the Contents folder in the install directory.
Also note that most Terraria textures are already loaded into the Terraria.GameContent.TextureAssets
class and can be used directly rather than loading a separate Asset
. Many of these require calling special methods such as Main.LoadItem
or Main.LoadNPC
to fully load, however, as they might not yet be loaded.
public class AssetTest : ILoadable
{
private static Asset<Texture2D> moddedTexture;
private static Asset<Texture2D> terrariaTexture;
void ILoadable.Load(Mod mod) {
moddedTexture = ModContent.Request<Texture2D>("ExampleMod/Content/SomeTexture");
moddedTexture = ModContent.GetInstance<ExampleMod>().Assets.Request<Texture2D>("Content/SomeTexture");
terrariaTexture = Main.Assets.Request<Texture2D>("Images/UI/Settings_Toggle");
terrariaTexture = ModContent.Request<Texture2D>("Images/UI/Settings_Toggle");
}
}
Mods can load additional assets in 2 ways, during mod loading or on demand. It is recommended to load additional assets during mod loading.
Assets loaded during mod loading will be fully loaded by the end of the mod loading process, ensuring that they are available to use immediately when needed in-game. The asset loading process will also cause the mod to fail to load if any asset fails to load. This will allow modders to quickly notice typos or formatting issues that would otherwise only appear as error messages in the in-game chat.
Assets loaded on demand in-game have the benefit of not being in RAM unless needed. They will never unload during a game session, but for some content it might be useful to only load assets when needed. Assets loaded on demand might require a little more effort by the modder to account for the asset load state.
The most common approach would be to have a Asset<T>
field in a ModType
or ILoadable
and to request the asset in Load
or SetStaticDefaults
. The field storing the Asset
will need to be static
for content that creates multiple instances of the same class, such as with ModItem
, ModProjectile
, and ModNPC
. In either case there is no need to set the Asset reference to null
in Unload
or manually Dispose
of the Asset.
public class ExampleHookProjectile : ModProjectile
{
private static Asset<Texture2D> chainTexture;
public override void Load() {
chainTexture = Mod.Assets.Request<Texture2D>("Content/Items/Tools/ExampleHookChain");
}
}
When loading an asset on demand, modders should try to call Request
once and store the resulting Asset
in a field. This can be done with a null check.
public class ExamplePlayerDrawLayer : PlayerDrawLayer
{
private Asset<Texture2D> exampleItemTexture;
protected override void Draw(ref PlayerDrawSet drawInfo) {
// Option A: Regular null check approach
if (exampleItemTexture == null) {
exampleItemTexture = Mod.Assets.Request<Texture2D>("Content/Items/ExampleItem");
}
// Option B: null-coalescing assignment operator approach
exampleItemTexture ??= Mod.Assets.Request<Texture2D>("Content/Items/ExampleItem");
// other code
drawInfo.DrawDataCache.Add(new DrawData(
exampleItemTexture.Value,
// other code
));
}
}
As an alternative to the if (texture == null)
check, modders can use exampleItemTexture ??= Mod.Assets.Request<Texture2D>("Content/Items/ExampleItem");
if they are familiar with the null-coalescing assignment operator syntax.
Modders should remember that the server does not load any assets. Most hooks that would deal with assets are not called on the server. Requesting assets on the server will return a dummy Asset
with a null
Value
. There is no need to gate code attempting to load assets with checks for !Main.dedServ
.
The default request mode is AsyncLoad
. Any assets requested during mod loading will be loaded completely by the time mod loading has completed. Asynchronous loading allows multiple assets to be loaded in parallel in the background, while the game continues to load mods or update/draw frames.
Assets requested asynchronously during gameplay will take at least one frame to load, but potentially multiple frames if the asset is large or many assets are requested simultaneously. Asset<Texture2D>
contains a DefaultValue
with a transparent 1x1 texture that will be returned from Asset.Value
while the asset is still loading. This allows mods to use the Value
in rendering without needing to check whether the asset has finished loading yet.
Note
As a demonstration, here is a mod loading hundreds of item textures during gameplay. When using AsyncLoad
, the scrolling is smooth and item textures appear as they are loading. When using ImmediateLoad
the scrolling is interrupted and the frame rate of the game suffers as the game spends time waiting for textures to load. This is an extreme example as it is loading hundreds of item textures at once, for normal modded content the textures loading in will be much less noticeable.
AsyncLoadVsImmediateLoadRecipeBrowserNoSound.mp4
There are situations where an asset might need to be loaded immediately. The most common situation is when the dimensions of the texture are required for UI layout (such as in the UIElement
/UIState
classes). If the element is initialized with an asset which hasn't finished loading, the dimensions of the temporary transparent 1x1 texture will be used and the resulting user interface will be incorrectly laid out. It is recommended to load the texture asynchronously during mod loading, and initialize the UIState
later, but AssetRequestMode.ImmediateLoad
can be passed to Request
or Asset.Wait()
can be called to ensure the asset is loaded.
Asset<Texture2D> buttonTexture = ModContent.Request<Texture2D>("ExampleMod/UI/Button", AssetRequestMode.ImmediateLoad);
UIImageButton button = new UIImageButton(buttonTexture);
Modders should not use AssetRequestMode.DoNotLoad
and instead follow the on-demand loading advice above. Terraria uses DoNotLoad
to populate the Terraria.GameContent.TextureAssets
fields with Asset<Texture2D>
entries that will be loaded only when needed. This necessitates helper methods which check whether the asset is loaded and re-request it such as Main.LoadItem/LoadNPC/LoadProjectile
.
Use Asset.IsLoaded
to check if the asset has finished loading.
This helper method is a bit shorter than calling Asset.Value.Size()
and will return Vector2.Zero
for assets which have not yet been fully loaded.
The Asset.Wait()
method can be used to force a texture previously requested to load immediately.
Some mods need to generate assets from other sources, like loading icons from a web-service. Modders can use the AssetRepository.CreateUntracked
method to create an Asset
from a Stream
containing a png/xnb/fxc/ogg/wav
etc directly. The resulting Asset
is "untracked" in the sense that it can not be retrieved by name via Request
.
If you copy code from Terraria that uses AssetRepository.Request
, you might notice that the vanilla code behaves as if the asset is loaded immediately, even though the default request mode is AsyncLoad
. This is because there is a special internal
overload of AssetRepository.Request
which uses ImmediateLoad
by default that keeps the vanilla code working, while we changed the default for modders and ModContent.Request
.