Examples » Engine » Assets » Introduction and Custom Bridges

Basic Assets plugin features and creating your own bridges.

This example demonstrates how we can create a custom asset bridge to load assets of a given type. More specifically, we'll go through how we can create a bridge which loads std::strings from text files.

We define our bridge class by inheriting from cubos::engine::FileBridge, which offers a simple interface for loading and saving assets from files.

class TextBridge : public FileBridge
{
public:
    TextBridge()
        : FileBridge(cubos::core::reflection::reflect<std::string>())
    {
    }

We pass typeid(std::string) to the base class, so that the engine knows which type of assets this bridge can load. Then, we'll need to implement both the loadFromFile and saveToFile methods.

In the first method, we receive the assets manager where we should store the loaded data, the handle to the asset we're loading, and a stream to read the file data from.

    bool loadFromFile(Assets& assets, const AnyAsset& handle, Stream& stream) override
    {
        // Dump the file's contents into a string.
        std::string contents;
        stream.readUntil(contents, nullptr);

        // Store the asset's data.
        assets.store(handle, std::move(contents));
        return true;
    }

In the second method, we receive the assets manager, the handle to the asset we're saving, and a stream to write the file data to.

    bool saveToFile(const Assets& assets, const AnyAsset& handle, Stream& stream) override
    {
        // Get the asset's data.
        auto contents = assets.read<std::string>(handle);

        // Write the data to the file.
        stream.print(*contents);
        return true;
    }
};

Now that we have our bridge type, we must register it with the assets manager before using it.

    cubos.startupSystem("setup bridge to load .txt files").tagged(assetsBridgeTag).call([](Assets& assets) {
        assets.registerBridge(".txt", std::make_unique<TextBridge>());
    });

After this system runs, any time we load an asset whose path ends with .txt, the assets manager will use our bridge to load it.

In this sample we have a file sample.txt on the assets/ directory containing the following text:

How are you holding up?
Because I'm a potato
<clap clap clap>

We also have a file sample.txt.meta, which describes the asset for the engine. In this case, we only need to specify its UUID, which was generated on a UUID generator website:

{
    "id": "6f42ae5a-59d1-5df3-8720-83b8df6dd536"
}

Then, we can load it from our code:

    // Assets are identified through UUIDs which are defined in their .meta files.
    static const Asset<std::string> SampleAsset = AnyAsset("6f42ae5a-59d1-5df3-8720-83b8df6dd536");

    cubos.startupSystem("access .txt asset").tagged(assetsTag).call([](const Assets& assets) {
        // Access the text asset - will be loaded automatically.
        auto text = assets.read(SampleAsset);
        Stream::stdOut.print(*text);
    });

Some care must be taken when registering bridges or loading assets during the startup phase. Systems which add bridges should be tagged with cubos.assets.bridge so that they run before any assets are loaded. Similarly, startup systems which load assets should be tagged with cubos.assets so that they run after all bridges have been registered.