Skip to content

50. Asset Management

Learn about the asset management feature that allows you to access asset data such as Texture, Font, and Audio from anywhere in your program.

50.1 Overview of Asset Management

  • Siv3D has an "asset management" feature that allows you to assign names to asset handles like Texture, Font, and Audio, and access them like global variables from anywhere in your program through those names

50.1.1 Steps for Handling Asset Management

  1. Asset "Registration"
  2. Asset "Loading" (optional)
  3. Asset "Usage"
  4. Asset "Release" (optional)
  5. Asset "Unregistration" (optional)

50.1.2 Registration

  • Register assets with the engine
  • Call the appropriate function for the asset type (whether it's a texture, audio, etc.), assign a unique name to the asset, and register information such as file names and properties
  • Unless otherwise specified, asset data is not constructed at this point, so registration does not increase memory consumption

50.1.3 Loading

  • Actually load the asset data
  • Specify the asset name, and the engine constructs asset data in memory according to the file name and properties given during asset registration
  • If the specified asset is already loaded, nothing is done
  • Options for asynchronous loading are also provided

50.1.4 Usage

  • Specify the asset name to get Texture or Audio, and use them to .draw() or .play() as usual
  • If the corresponding asset is not loaded, loading is automatically performed at this timing
  • If the specified asset is not registered or is being loaded asynchronously, an empty Texture or Audio is returned

50.1.5 Release

  • Release asset data from memory while keeping registration information
  • Since the asset registration information remains after release, you can load or use it again
  • It's good to release assets when you won't use a once-loaded asset for a while and want to reduce memory consumption

50.1.6 Unregistration

  • Delete asset registration information and name from asset management
  • If the corresponding asset is not released, it is automatically released
  • When the application terminates, all assets are automatically released and unregistered, so explicit unregistration is not necessary

50.1.7 Functions for Various Operations

Code Description
Register(name, ...) Register an asset
IsRegistered(name) Returns whether an asset is registered
Load(name) Load an asset
LoadAsync(name) Start asynchronous loading of an asset
Wait(name) Wait until asynchronous loading of an asset is complete
IsReady(name) Returns whether asset loading is complete (regardless of success or failure)
Release(name) Release an asset
Unregister(name) Unregister an asset
ReleaseAll() Release all registered assets
UnregisterAll() Unregister all registered assets
Enumerate() Enumerate information list of all registered assets

50.2 Texture Assets

  • When handling Texture through asset management, use functions that start with TextureAsset::
  • Access Texture assets with TextureAsset(name)
  • The following sample code registers example/windmill.png with the name "Windmill", registers example/siv3d-kun.png with the name "Siv3D-kun", and registers the emoji 🐈 with the name "Cat"

# include <Siv3D.hpp>

void Draw()
{
	// Use Texture assets
	TextureAsset(U"Windmill").draw(40, 40);
	TextureAsset(U"Siv3D-kun").scaled(0.8).drawAt(300, 300);
	TextureAsset(U"Cat").drawAt(600, 400);
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// Register Texture assets
	TextureAsset::Register(U"Windmill", U"example/windmill.png");
	TextureAsset::Register(U"Siv3D-kun", U"example/siv3d-kun.png", TextureDesc::Mipped);
	TextureAsset::Register(U"Cat", U"🐈"_emoji);

	while (System::Update())
	{
		Draw();
	}
}

50.3 Advanced Texture Assets

  • When registering complex Texture assets that create textures from Image or perform preprocessing during loading, use TextureAssetData
  • Create an empty TextureAssetData and set a function object with the signature bool(TextureAssetData& asset, const String&) that describes the loading process in the onLoad member variable
  • This function object is automatically called when the asset is loaded
  • You are required to assign a texture to asset.texture to construct the asset data
  • The following sample uses TextureAssetData to register images generated by the program and reduced versions of images loaded from files as textures
    • The Image class is explained in detail in Tutorial 63

# include <Siv3D.hpp>

std::unique_ptr<TextureAssetData> MakeTextureAssetData1()
{
	// Create empty texture asset data
	std::unique_ptr<TextureAssetData> assetData = std::make_unique<TextureAssetData>();

	// Set the loading job
	assetData->onLoad = [](TextureAssetData& asset, const String&)
		{
			// Assign texture to asset data
			asset.texture = Texture{ Image{ 256, 256, Palette::Seagreen },  TextureDesc::Mipped };
			return static_cast<bool>(asset.texture);
		};

	return assetData;
}

std::unique_ptr<TextureAssetData> MakeTextureAssetData2(const FilePath& path, const TextureDesc textureDesc)
{
	// Create empty texture asset data
	std::unique_ptr<TextureAssetData> assetData = std::make_unique<TextureAssetData>();

	// Assign file path
	assetData->path = path;

	// Assign texture settings
	assetData->desc = textureDesc;

	// Set the loading job
	assetData->onLoad = [](TextureAssetData& asset, const String&)
		{
			// Scale the image from the specified file path by 0.5 and make it a texture
			asset.texture = Texture{ Image{ asset.path }.scaled(0.5), asset.desc };
			return static_cast<bool>(asset.texture);
		};

	return assetData;
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// Register assets (using custom texture asset data)
	TextureAsset::Register(U"MyTexture1", MakeTextureAssetData1());
	TextureAsset::Register(U"MyTexture2", MakeTextureAssetData2(U"example/windmill.png", TextureDesc::Mipped));
	TextureAsset::Register(U"MyTexture3", MakeTextureAssetData2(U"example/siv3d-kun.png", TextureDesc::Mipped));

	while (System::Update())
	{
		TextureAsset(U"MyTexture1").draw(100, 100);
		TextureAsset(U"MyTexture2").draw(300, 300);
		TextureAsset(U"MyTexture3").draw(400, 200);
	}
}

50.4 Font Assets

  • When handling Font through asset management, use functions that start with FontAsset::
  • Access Font assets with FontAsset(name)

# include <Siv3D.hpp>

void Draw()
{
	// Use assets
	FontAsset(U"Title")(U"My Game").drawAt(80, Vec2{ 400, 100 }, Palette::Seagreen);
	FontAsset(U"Menu")(U"Play").drawAt(40, Vec2{ 400, 400 }, ColorF{ 0.1 });
	FontAsset(U"Menu")(U"Exit").drawAt(40, Vec2{ 400, 500 }, ColorF{ 0.1 });
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// Register assets
	FontAsset::Register(U"Title", FontMethod::MSDF, 48, U"example/font/RocknRoll/RocknRollOne-Regular.ttf");
	FontAsset::Register(U"Menu", FontMethod::MSDF, 48, Typeface::Bold);

	while (System::Update())
	{
		Draw();
	}
}

50.5 Audio Assets

  • When handling Audio through asset management, use functions that start with AudioAsset::
  • Access Audio assets with AudioAsset(name)
# include <Siv3D.hpp>

void PlayPiano()
{
	// Use assets
	AudioAsset(U"Piano").playOneShot();
}

void PlayShot()
{
	// Use assets
	AudioAsset(U"SE").playOneShot();
}

void Main()
{
	// Register assets
	AudioAsset::Register(U"BGM", Audio::Stream, U"example/test.mp3");
	AudioAsset::Register(U"SE", U"example/shot.mp3");
	AudioAsset::Register(U"Piano", GMInstrument::Piano1, PianoKey::A4, 0.5s);

	// Use assets
	AudioAsset(U"BGM").setVolume(0.2);
	AudioAsset(U"BGM").play();

	while (System::Update())
	{
		if (MouseL.down())
		{
			PlayPiano();
		}

		if (MouseR.down())
		{
			PlayShot();
		}
	}
}

50.6 Preloading

  • Using Load() for each asset immediately loads the asset if it is not loaded
  • If you want to prevent frame time spikes caused by loading assets during game progress, you can use this function to preload on loading screens, etc.
  • In FontAsset::Load(), you can also pass text to preload
  • You can check if asset loading is complete with IsReady()

# include <Siv3D.hpp>

void Main()
{
	const String preloadText = U"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

	FontAsset::Register(U"MyFont", FontMethod::MSDF, 48, Typeface::Bold);
	TextureAsset::Register(U"MyTexture", U"example/bay.jpg");
	AudioAsset::Register(U"MyAudio", Audio::Stream, U"example/test.mp3");
	AudioAsset::Register(U"MyMIDI", U"example/midi/test.mid");

	// Preload
	FontAsset::Load(U"MyFont", preloadText);
	TextureAsset::Load(U"MyTexture");
	AudioAsset::Load(U"MyAudio");
	AudioAsset::Load(U"MyMIDI");

	// Confirm that loading is complete
	Print << FontAsset::IsReady(U"MyFont");
	Print << TextureAsset::IsReady(U"MyTexture");
	Print << AudioAsset::IsReady(U"MyAudio");
	Print << AudioAsset::IsReady(U"MyMIDI");

	while (System::Update())
	{

	}
}

50.7 Asynchronous Loading

  • Using LoadAsync() for each asset starts asynchronous loading of the asset using a separate thread if the asset is not loaded
  • This avoids blocking the main thread processing during asset loading
  • You can check if asynchronous loading of an asset is complete with IsReady()
  • Wait() causes the main thread to wait until loading is complete
  • If you access an asset during asynchronous loading, an empty asset is returned
    • Especially with Audio assets, playing empty assets can produce unexpected sounds (Tutorial 41.7), so caution is needed

Note for OpenGL Backend

  • With the OpenGL backend (default for macOS and Linux, and when selected on Windows), asynchronous loading of TextureAsset progresses within System::Update()
  • During asynchronous loading of TextureAsset, please call System::Update() at the usual frequency

# include <Siv3D.hpp>

void Main()
{
	const String preloadText = U"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

	FontAsset::Register(U"MyFont", FontMethod::MSDF, 48, Typeface::Bold);
	TextureAsset::Register(U"MyTexture", U"example/bay.jpg");
	AudioAsset::Register(U"MyAudio", Audio::Stream, U"example/test.mp3");
	AudioAsset::Register(U"MyMIDI", U"example/midi/test.mid");

	// Start asynchronous loading
	FontAsset::LoadAsync(U"MyFont", preloadText);
	TextureAsset::LoadAsync(U"MyTexture");
	AudioAsset::LoadAsync(U"MyAudio");
	AudioAsset::LoadAsync(U"MyMIDI");

	while (System::Update())
	{
		ClearPrint();

		// Check if loading is complete
		Print << FontAsset::IsReady(U"MyFont");
		Print << TextureAsset::IsReady(U"MyTexture");
		Print << AudioAsset::IsReady(U"MyAudio");
		Print << AudioAsset::IsReady(U"MyMIDI");
	}
}

50.8 Asset List and Tags

  • In Register(), you can register asset names and asset tags with { assetName, { assetTag, ... } }
  • Combined with ::Enumerate() which gets a list of registered assets, this makes asset management convenient for loading and releasing assets with specific tags

# include <Siv3D.hpp>

void Main()
{
	AudioAsset::Register({ U"BGM-0", { U"BGM" } }, Audio::Stream, U"example/test.mp3");
	AudioAsset::Register({ U"BGM-1", { U"BGM" } }, U"example/midi/test.mid");
	AudioAsset::Register({ U"PianoC", { U"SE", U"Piano" } }, GMInstrument::Piano1, PianoKey::C4, 0.5s);
	AudioAsset::Register({ U"PianoD", { U"SE", U"Piano" } }, GMInstrument::Piano1, PianoKey::D4, 0.5s);
	AudioAsset::Register({ U"PianoE", { U"SE", U"Piano" } }, GMInstrument::Piano1, PianoKey::E4, 0.5s);
	AudioAsset::Register({ U"TrumpetC", { U"SE", U"Trumpet" } }, GMInstrument::Trumpet, PianoKey::C4, 0.5s);
	AudioAsset::Register({ U"TrumpetD", { U"SE", U"Trumpet" } }, GMInstrument::Trumpet, PianoKey::D4, 0.5s);
	AudioAsset::Register({ U"TrumpetE", { U"SE", U"Trumpet" } }, GMInstrument::Trumpet, PianoKey::E4, 0.5s);

	for (auto&& [name, info] : AudioAsset::Enumerate())
	{
		Print << name << U": " << info.tags;

		// Load only assets with the "SE" tag
		if (info.tags.includes(U"SE"))
		{
			AudioAsset::Load(name);
		}
	}

	Print << U"---";
	Print << AudioAsset::IsReady(U"BGM-0");
	Print << AudioAsset::IsReady(U"BGM-1");
	Print << AudioAsset::IsReady(U"PianoC");
	Print << AudioAsset::IsReady(U"PianoD");
	Print << AudioAsset::IsReady(U"PianoE");
	Print << AudioAsset::IsReady(U"TrumpetC");
	Print << AudioAsset::IsReady(U"TrumpetD");
	Print << AudioAsset::IsReady(U"TrumpetE");

	while (System::Update())
	{

	}
}