LibGDX behavior tree text format as loadable asset

In our game, we have decided to use LibGDX-AI library for its behavior tree. One advantage of this library is that it comes with a custom text format to easily create and load behavior trees. Even non programmers can edit those files.

Here’s a simple definition of the text format :

The behavior tree text format recognized by the API is a simple and versatile indentation-based format to load behavior trees from an external resource (usually a file) in a data-driven programming style.

I thought of using that text format (that I name .btree like they do in examples) to store behavior trees as assets. However, I want jMonkey’s asset manager to be able to load those custom assets, mainly because it deals perfectly with already loaded assets, i.e. it doesn’t load the same asset twice.

I thought I could implements the Savable interface, code the read method and code the write method but it won’t work as it saves key-value data. It would only slow down the process of loading the trees and make it hard to edit manually. Also, I don’t want the behavior data to be changed by the exporter and importer.

All I need for LibGDX-AI is a Reader of the asset. I finally found out that I could do it with the registerLoader method from the AssetManager and create our own loader.

package com.chevreuilgames.retroflashyrpg.assets.loader.behaviortree;

import com.badlogic.gdx.ai.btree.BehaviorTree;
import com.badlogic.gdx.ai.btree.utils.BehaviorTreeParser;
import com.chevreuilgames.retroflashyrpg.assets.AssetType;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;

import java.io.IOException;
import java.io.InputStreamReader;

/**
 * The <code>AssetLoader</code> for LibGDX-AI's behavior tree text format.
 */
public class BehaviorTreeLoader implements AssetLoader {

    public static final AssetType ASSET_TYPE = AssetType.BEHAVIOR;

    @Override
    public Object load(AssetInfo assetInfo) throws IOException {
        BehaviorTree tree = null;
        InputStreamReader in = null;
        
        try {
            in = new InputStreamReader(assetInfo.openStream());
            BehaviorTreeParser parser = new BehaviorTreeParser(BehaviorTreeParser.DEBUG_NONE);
            tree = parser.parse(in, null);
        } finally {
            if (in != null) {
                in.close();
            }
        }

        return tree;
    }
}

However, still one problem perists :

Important: Unoptimized external model files (.mesh.xml, .material, .obj, .mat, .blend, etc) are not bundled by the default build script into the final game builds in the dist directory! If you or your customers try to run games containing code that loads non-.j3o models, you get a AssetNotFoundException Runtime Error (resource not found). Your final application code should only reference .j3o files. – Note that your developers will not get this runtime error when running development builds straight from the SDK.

Does this mean that my custom asset extension, which is not a .j3o, will be omitted?

Also, does implementing the AssetLoader interface and coding the load method make the asset be loaded and kept in memory only once?

I’d also like to make the behavior tree implements the CloneableSmartAsset but I don’t want to change the source :confused: Is there a way around this?

It means you need to make sure to configure the assets jar to bundle the other extensions. I think it specifically filters those other things out rather than only including specific things. I know because back when I used the SDK I was constantly adding more filters for psd, wav, etc…

Test it and see.

Wrap it in something that can implement CloneableSmartAsset and use that instead.

1 Like

That’s what I’m actually doing. It’s quite a big architecture and this is what I’ve done so far :

  1. BehaviorTreeAsset
  2. BehaviorTreeAssetLoader
  3. BehaviorTreeAssetKey
  4. BehaviorTreeAssetProcessor
  5. Register the loader with its extension(s)
  6. What I’ll do next : loading my custom asset with assetManager.loadAsset(new BehaviorTreeAssetKey(...))

LibGDX-AI’s behavior trees support cloning, so I had to use a processor.

Ok thanks.

We actually don’t have an asset jar. The assets are included in the project classpath. We’ve got a big architecture too with many subprojects in gradle and asset metadata generation. So, to include models, my friend import them in a dummy SDK project and then move them into our asset project because the SDK hardcode the path. If we were to add filters like you described, that would be in the SDK? So, in other words, because we don’t actually use the asset jar, we don’t need filters?

Yes. If you aren’t using the SDK to bundle an assets jar then you don’t have to worry about the default filters that the SDK used to bundle an assets jar.

1 Like