Skip to content

Files

271 lines (218 loc) · 7.08 KB

custom-asset.md

File metadata and controls

271 lines (218 loc) · 7.08 KB

HOWTO: Create a custom asset type

Flax uses two types of assets:

  • Binary asset (files with extension .flax)
  • Json assets (files with extension .json/.scene/etc.)

Binary assets are better choice for textures, models and bigger types in general. While json assets are useful when dealing with data that can be used directly by scripts and scene objects.

In this tutorial you will learn how to define a custom json asset type and use it in your game.

1. Define a data class

Implement a class that will define the asset data layout. In this example we store some supported screen resolutions and the default language. Then it will be saved to json and modified in editor. Later game can load asset and use its data.

public class MySettings
{
	public Vector2[] SupportedResolutions =
	{
		new Vector2(1280, 720),
		new Vector2(1920, 1080),
	};

	public string DefaultLanguage = "en";
}
#pragma once

#include "Engine/Core/ISerializable.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Scripting/ScriptingType.h"

API_CLASS() class GAME_API MySettings : public ISerializable
{
    API_AUTO_SERIALIZATION();
    DECLARE_SCRIPTING_TYPE_NO_SPAWN(MySettings);

    API_FIELD()
	Array<Vector2> SupportedResolutions = { Vector2(1280, 720), Vector2(1920, 1080) };

    API_FIELD()
	String DefaultLanguage = TEXT("en");
};

Add this class to game scripts assembly. It can be in editor scripts assembly but then it will be design-time only.

2. Create an asset

Next step is to create an actual asset (.json file) that contains settings. In project Content use right-click and use option New -> Json Asset. Then specify it's name and pick the type to created class typename (in this example it's MySettings). Press Create button to make a file with default values of the type.

New Asset picker

Also, you can use Custom Editor or Custom Window or just editor-only code to spawn a new asset in editor.

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : GenericEditor
{
	public override void Initialize(LayoutElementsContainer layout)
	{
		base.Initialize(layout);

		layout.Space(20);
		var button = layout.Button("Click me", Color.Green);
		button.Button.Clicked += OnButtonClicked;
	}

	private void OnButtonClicked()
	{
		// Create json asset
		FlaxEditor.Editor.SaveJsonAsset("Content/mySettings.json", new MySettings());
	}
}

After selecting the script and pressing the custom editor button the asset appears in the Content folder.

Tutorial

3. Edit asset in editor

Double-click on created asset. Dedicated editor window will pop up. Use it to modify the asset and press Save button to save the data.

Tutorial

Json asset file contents:

{
	"ID": "a71da43c4c1905f17c1104978df8070f",
	"TypeName": "MySettings",
	"EngineBuild": 6147,
	"Data": {
	"SupportedResolutions": [
		{
			"X": 1280.0,
			"Y": 720.0
		},
		{
			"X": 1920.0,
			"Y": 1080.0
		},
		{
			"X": 640.0,
			"Y": 480.0
		}
	],
	"DefaultLanguage": "en"
}
}

4. Use asset in game

Using json asset works the same in editor and in built game. The difference is that during game cooking json assets are compressed and encrypted so data is secure.

To use this asset simply add JsonAsset reference to your script and drag and drop the mySettings.json asset to it.

public class MyScript : Script
{
	public JsonAssetReference<MySettings> MySettings;

	public override void OnStart()
	{
		if (MySettings)
		{
			var obj = MySettings.Instance;
			Debug.Log("Default language: " + obj.DefaultLanguage);
		}
	}
}
#pragma once

#include "Engine/Scripting/Script.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/JsonAssetReference.h"

API_CLASS() class GAME_API MyScript : public Script
{
    API_AUTO_SERIALIZATION();
    DECLARE_SCRIPTING_TYPE(MyScript);

    API_FIELD() JsonAssetReference<::MySettings> MySettings;

    // [Script]
    void OnStart() override
    {
        const auto obj = MySettings.GetInstance();
        if (obj)
        {
            LOG(Info, "Default language: {0}", obj->DefaultLanguage);
        }
    }
};

inline MyScript::MyScript(const SpawnParams& params)
    : Script(params)
{
}

Tutorial

Asset creation utility

If you develop 3rd Party SDK plugin or commonly used asset type then you can use ContentContextMenu attribute to link it into the Editor's Content window.

[ContentContextMenu("New/My Settings")]
public class MySettings
{
...
}
API_CLASS(Attributes="ContentContextMenu(\"New/My Settings\")")
class GAME_API MySettings : public ISerializable
{
...
};

Asset extension in Editor

Flax Editor supports extending editing and usage experience per-asset type. For example, you can override the default asset icon, generate a thumbnail based on contents or provide additional actions executable from Content window.

Example C# code for Editor extending the MySettings asset:

public class MySettingsItem : JsonAssetItem
{
    /// <inheritdoc />
    public MySettingsItem(string path, Guid id, string typeName)
    : base(path, id, typeName)
    {
        // Use custom icon (Sprite)
        _thumbnail = Editor.Instance.Icons.Document128;
    }
}

[ContentContextMenu("New/My Settings")]
public class MySettingsProxy : SpawnableJsonAssetProxy<MySettings>
{
    /// <inheritdoc />
    public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
    {
        // Use custom type of the Asset Item for editor
        return new MySettingsItem(path, id, typeName);
    }
}

You can easily customizable proxy methods by overriding them.

Then register custom asset proxy within Editor plugin initialization (ensure to remove it on game code unloading - eg. during script hot-reload in Editor):

public class MyEditorPlugin : EditorPlugin
{
    private MySettingsProxy _proxy;

    /// <inheritdoc />
    public override void InitializeEditor()
    {
        _proxy = new MySettingsProxy();
        Editor.ContentDatabase.AddProxy(_proxy, true);
    }

    /// <inheritdoc />
    public override void DeinitializeEditor()
    {
        Editor.ContentDatabase.RemoveProxy(_proxy, true);
        _proxy = null;

        base.DeinitializeEditor();
    }
}

Another simple way to add a custom icon to a JsonAsset without creating a new proxy is as follows.

public class MyEditorPlugin : EditorPlugin
{
    public override void InitializeEditor()
    {
        base.InitializeEditor();
		var atlas = Content.Load<SpriteAtlas>("Content/ExampleSpriteAtlas.flax");
        var spriteHandle = new SpriteHandle(atlas, 0);
        Editor.ContentDatabase.AddProxy(new SpawnableJsonAssetProxy<MySettings>(spriteHandle));
        Editor.ContentDatabase.Rebuild(true);
    }
}