Skip to content

annulusgames/Alchemy

Repository files navigation

Alchemy

license

日本語版READMEはこちら

Overview

Alchemy is a library that provides Inspector extensions using attributes.

In addition to providing easy and powerful attribute-based editor extensions, it allows serialization of any types (Dictionary, HashSet, Nullable, Tuple, etc.) via its own serialization system, so those types can be edited in the Inspector. Alchemy works simply by adding attributes to the target type — just mark it as partial, and a Source Generator creates the necessary code. Unlike Odin, there is no need to inherit from dedicated base classes.

v2.0 also adds EditorWindow and Hierarchy extensions. These make it easy to build tools that streamline your editor workflow.

Features

  • Add over 30 attributes to extend the Inspector
  • Support SerializeReference, allowing selection of types from a dropdown
  • Serialize any type (Dictionary, HashSet, Nullable, Tuple, etc.) and edit them in the Inspector
  • Create EditorWindows using attributes
  • Improve Hierarchy usability
  • Create custom attributes that work with Alchemy

Setup

Requirements

  • Unity 2021.2 or higher (Recommended: 2022.1 or higher for serialization extensions)
  • Unity Serialization 2.0 or higher (for serialization extensions)

Installation

  1. Open Package Manager from Window > Package Manager
  2. Click the "+" button > Add package from git URL
  3. Enter the following URL:
https://github.com/annulusgames/Alchemy.git?path=/Alchemy/Assets/Alchemy

Or open Packages/manifest.json and add the following to the dependencies block:

{
    "dependencies": {
        "com.annulusgames.alchemy": "https://github.com/annulusgames/Alchemy.git?path=/Alchemy/Assets/Alchemy"
    }
}

Documentation

The full documentation can be found here.

Basic Usage

To customize the display in the Inspector, add attributes to the class fields.

using UnityEngine;
using UnityEngine.UIElements;
using Alchemy.Inspector;

public class AttributesExample : MonoBehaviour
{
    [LabelText("Custom Label")]
    public float foo;

    [HideLabel]
    public Vector3 bar;
    
    [AssetsOnly]
    public GameObject baz;

    [Title("Title")]
    [HelpBox("HelpBox", HelpBoxMessageType.Info)]
    [ReadOnly]
    public string message = "Read Only";
}

Attributes for grouping fields are also available. Groups can be nested by separating group names with a slash /.

using UnityEngine;
using Alchemy.Inspector;

public class GroupAttributesExample : MonoBehaviour
{
    [FoldoutGroup("Foldout")]
    public int a;

    [FoldoutGroup("Foldout")]
    public int b;

    [FoldoutGroup("Foldout")]
    public int c;

    [TabGroup("Tab", "Tab1")]
    public int x;

    [TabGroup("Tab", "Tab2")]
    public string y;

    [TabGroup("Tab", "Tab3")]
    public Vector3 z;
}

By adding the [Button] attribute to a method, you can execute the method from the Inspector.

using System.Text;
using UnityEngine;
using Alchemy.Inspector;

[Serializable]
public sealed class Example : IExample
{
    public float foo;
    public Vector3 bar;
    public GameObject baz;
}

public class ButtonExample : MonoBehaviour
{
    [Button]
    public void Foo()
    {
        Debug.Log("Foo");
    }

    [Button]
    public void Foo(int parameter)
    {
        Debug.Log("Foo: " + parameter);
    }

    [Button]
    public void Foo(Example parameter)
    {
        var builder = new StringBuilder();
        builder.AppendLine();
        builder.Append("foo = ").AppendLine(parameter.foo.ToString());
        builder.Append("bar = ").AppendLine(parameter.bar.ToString());
        builder.Append("baz = ").Append(parameter.baz == null ? "Null" : parameter.baz.ToString());
        Debug.Log("Foo: " + builder.ToString());
    }
}

Alchemy provides many other attributes. The list of available attributes can be found in the documentation.

Editing Interfaces and Abstract Classes

Alchemy supports Unity's SerializeReference. By adding the [SerializeReference] attribute, interfaces and abstract classes can be edited in the Inspector.

using UnityEngine;

public interface IExample { }

[Serializable]
public sealed class ExampleA : IExample
{
    public float alpha;
}

[Serializable]
public sealed class ExampleB : IExample
{
    public Vector3 beta;
}

[Serializable]
public sealed class ExampleC : IExample
{
    public GameObject gamma;
}

public class SerializeReferenceExample : MonoBehaviour
{
    [SerializeReference] public IExample Example;
    [SerializeReference] public IExample[] ExampleArray;
}

Interfaces and abstract classes are displayed as shown above, and you can select concrete types from the dropdown to instantiate them.

For more details, refer to SerializeReference.

Hierarchy

Alchemy provides several features that extend the Hierarchy.

Toggles and Icons

You can add toggles for each object's active/inactive state and icons that show its components to the Hierarchy. These can be configured from Project Settings.

Decoration

From the Create menu, you can create objects that decorate the Hierarchy.

These objects are automatically excluded from builds. (If they have child objects, any child objects are unparented before deletion.) For more details, refer to Decorating Hierarchy.

AlchemyEditorWindow

By inheriting from the AlchemyEditorWindow class instead of EditorWindow, you can create editor windows using Alchemy attributes.

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using Alchemy.Editor;
using Alchemy.Inspector;

public class EditorWindowExample : AlchemyEditorWindow
{
    [MenuItem("Window/Example")]
    static void Open()
    {
        var window = GetWindow<EditorWindowExample>("Example");
        window.Show();
    }
    
    [Serializable]
    [HorizontalGroup]
    public class DatabaseItem
    {
        [LabelWidth(30f)]
        public float foo;

        [LabelWidth(30f)]
        public Vector3 bar;
        
        [LabelWidth(30f)]
        public GameObject baz;
    }

    [ListViewSettings(ShowAlternatingRowBackgrounds = AlternatingRowBackground.All, ShowFoldoutHeader = false)]
    public List<DatabaseItem> items;

    [Button, HorizontalGroup]
    public void Button1() { }

    [Button, HorizontalGroup]
    public void Button2() { }

    [Button, HorizontalGroup]
    public void Button3() { }
}

Data for windows that inherit from AlchemyEditorWindow is saved as JSON in the project's ProjectSettings folder. For more details, refer to Saving Editor Window Data.

Using Serialization Extensions

If you want to edit types that Unity cannot serialize, such as Dictionary, you can use the [AlchemySerialize] attribute to serialize these types.

Serialization extensions require the Unity.Serialization package. Additionally, reflection-based serialization using Unity.Serialization may not work in AOT environments prior to Unity 2022.1. Check the package manual for details.

The following example uses Alchemy's serialization extension to make various types serializable and editable in the Inspector.

using System;
using System.Collections.Generic;
using UnityEngine;
using Alchemy.Serialization;

// By adding the [AlchemySerialize] attribute, Alchemy's serialization extension is enabled.
// It can be used with any type, regardless of its base class, but the target type must be partial for the Source Generator to generate code.
[AlchemySerialize]
public partial class AlchemySerializationExample : MonoBehaviour
{
    // Add [AlchemySerializeField] and [NonSerialized] attributes to the target fields.
    [AlchemySerializeField, NonSerialized]
    public HashSet<GameObject> hashset = new();

    [AlchemySerializeField, NonSerialized]
    public Dictionary<string, GameObject> dictionary = new();

    [AlchemySerializeField, NonSerialized]
    public (int, int) tuple;

    [AlchemySerializeField, NonSerialized]
    public Vector3? nullable = null;
}

For technical details on the serialization process, refer to Alchemy Serialization Process in the documentation.

Help

Unity forum: https://forum.unity.com/threads/released-alchemy-inspector-serialization-extensions.1523665/

License

MIT License

Releases

Packages

Used by

Contributors

Languages