• Stars
    star
    160
  • Rank 234,703 (Top 5 %)
  • Language
    C#
  • License
    MIT License
  • Created almost 2 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

UniTyped is a source generator that allows typed access to data such as SerializedObjects, material parameters, tags and layers.

UniTyped

日本語版

UniTyped is a source generator that allows typed access to data such as SerializedObjects, material parameters, tags and layers. It helps you write more concise and safe code.

  • Statically Typed: Eliminates string-based unstable data accesses and allows accesses to non-existent data to be discovered at the compile stage. There is no need to write a large number of FindPropertys or deal with the complicated API of SerializedProperty.
  • Less Heap Allocations: Generated code is struct-based and designed to avoid boxing.

Table of Contents

Requirements

  • Fully supported in Unity 2021.2 and later
  • In Unity 2021.1 and below, UniTyped features can be used with Manual Generator

Installation

Add git URL https://github.com/ruccho/UniTyped.git?path=/Packages/com.ruccho.unityped from UPM.

Limitations

  • This project is currently experimental and breaking changes may be made.
  • It may not cover all use cases. Please let me know by issues if you notice anything.

Features

Typed View Generation

Serialized Objects

Put [UniTyped.UniTyped] attribute on your MonoBehaviour, ScriptableObject or your custom serializable class / struct then you can use UniTyped.Generated.[YourNamespace].[YourClass]View struct in your editor code.

using UnityEngine;
using UniTyped;
using UniTyped.Generated;

[UniTyped]
public class Example : MonoBehaviour
{
    [SerializeField] private int someValue = 0;
}

#if UNITY_EDITOR

[UnityEditor.CustomEditor(typeof(Example))]
public class ExampleEditor : UnityEditor.Editor
{
    public override void OnInspectorGUI()
    {
        //ExampleView is auto-generated struct.
        var view = new ExampleView()
        {
            Target = serializedObject
        };
        
        //equivalent to serializedObject.FindProperty("someValue").intValue++;
        view.someValue++;

        serializedObject.ApplyModifiedProperties();
        
    }
}

#endif

Array / List operation

using System;
using System.Collections.Generic;
using UnityEngine;
using UniTyped;

[UniTyped]
public class Example : MonoBehaviour
{
    [SerializeField] private int[] someArray = default;
    [SerializeField] private List<int> someList = default;
}


#if UNITY_EDITOR

[UnityEditor.CustomEditor(typeof(Example))]
public class ExampleEditor : UnityEditor.Editor
{
    public override void OnInspectorGUI()
    {
        var view = new UniTyped.Generated.ExampleView()
        {
            Target = serializedObject
        };

        // array access
        for (int i = 0; i < view.someArray.Length; i++)
        {
            Debug.Log(view.someArray[i].Value);
        }

        //also accessible with IEnumerator<T>
        foreach (var element in view.someArray)
        {
            Debug.Log(element.Value);
        }

        serializedObject.ApplyModifiedProperties();

        base.OnInspectorGUI();
    }
}

#endif

Configure Code Generation

Use [UniTypedField] attribute to control the result of code generation.

  • ignore: ignores the field.
  • nestedField: By default, UniTyped flattens accessor property to allow direct manipulation of values, but this option avoids that and exposes internal view. It is useful to access SerializedProperty of specific field.
using UnityEngine;
using UniTyped;
using UniTyped.Generated;


[UniTyped]
public class Example : MonoBehaviour
{
    [SerializeField, UniTypedField(ignore = true)]
    private int ignoredField = 0;
    
    [SerializeField, UniTypedField(forceNested = true)]
    private int nestedField = 0;
}

#if UNITY_EDITOR

[UnityEditor.CustomEditor(typeof(Example))]
public class ExampleEditor : UnityEditor.Editor
{
    public override void OnInspectorGUI()
    {
        var view = new ExampleView()
        {
            Target = serializedObject
        };

        
        view.ignoredField++; // error: Cannot resolve symbol 'ignoreField'

        Debug.Log(view.nestedField.Value); // int Value { get; set; }
        Debug.Log(view.nestedField.Property); // SerializedProperty Property { get; set; }
        

        serializedObject.ApplyModifiedProperties();
        
    }
}
#endif

Materials

Create partial struct with UniTyped.UniTypedMaterialView attribute.

using UnityEngine;
using UniTyped;

//specify relative path from this source file
[UniTypedMaterialView("NewUnlitShader.shader")]
public partial struct NewUnlitShaderView
{
}

//ShaderGraph is also supported.
/*
[UniTypedMaterialView("New Shader Graph.shadergraph")]
public partial struct NewShaderGraphView
{
}
*/

public class MaterialViewExample : MonoBehaviour
{
    [SerializeField] private Material mat = default;

    void Update()
    {
        var view = new NewUnlitShaderView()
        {
            Target = mat
        };

        view._Color = Color.HSVToRGB(Time.time % 1f, 1f, 1f);

    }
}
Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1, 1, 1, 1)
    }

    //...
}

Animator Parameters

Create partial struct with UniTyped.UniTypedAnimatorView attribute. Ensure that the latest aniamtor controller assets are saved to disk (with Save Project) .

image

using UnityEngine;
using UniTyped;

[UniTypedAnimatorView("New Animator Controller.controller")]
public partial struct NewAnimatorControllerView
{
    
}

public class AnimatorViewExample : MonoBehaviour
{

    private Animator animator;
    
    void Update()
    {
        if (!animator && !TryGetComponent(out animator)) return;
        
        var view = new NewAnimatorControllerView()
        {
            Target = animator
        };

        // Float
        view.FloatParameter = Time.time;
        view.SetFloatParameter(Time.time, dampTime, deltaTime);
        
        // Int
        view.IntParameter = Time.frameCount;
        
        // Bool
        view.BoolParameter = true;
        
        // Trigger
        view.TriggerA();
        view.ResetTriggerA();
    }
}

Tags and Layers Enums

In UniTyped.Reflection namespace, Tags, Layers and SortingLayers enums are automatically defined.

You have to recompile UniTyped assembly after you make changes in tags and layers in project settings because these members are generated within UniTyped assembly. To recompile it, use Assets > UniTyped > Apply Tags and Layers Reflection option in menu bar.

using System.Collections.ObjectModel;
using UnityEngine;
using UniTyped.Reflection;

public class TagsAndLayersExample : MonoBehaviour
{
    private void Start()
    {
        // ---Tags---
        
        Debug.Log(Tags.New_tag);
        
        // tag names can be queried with UniTyped.Reflection.TagUtility
        Debug.Log(TagUtility.GetTagName(Tags.New_tag)); // "New tag"
        TagUtility.TryGetTagValue("New tag", out Tags result); // result: Tags.New_tag
        ReadOnlyCollection<string> tagNames = TagUtility.TagNames; // enumerate tag names
        
        
        // ---Layers---
        
        Debug.Log(Layers.Default);
        Debug.Log(Layers.UI);
        Debug.Log(Layers.Water);
        Debug.Log(Layers.Ignore_Raycast);
        Debug.Log(Layers.TransparentFX);
        
        // layer enum values can be used as layer indices.
        Debug.Log(LayerMask.LayerToName((int)Layers.Default));
        
        
        // ---Sorting Layers---
        
        Debug.Log(SortingLayers.Default);
        
        // sorting layer enum values can be used as sorting layer IDs.
        SortingLayer.GetLayerValueFromID((int)SortingLayers.Default);
    }
}

Manual Generator

By default, UniType uses the functionality of the Roslyn source generator available in Unity 2021.2 and later. In Unity 2021.1 and below, you can use UniType with manual generator provided as individual package.

Requirements

  • .NET runtime (supports netcoreapp3.1 target)
    • ensure dotnet cli tool is available
  • MSBuild (included in Visual Studio or .NET SDK)

Installation

Add git URL https://github.com/ruccho/UniTyped.git?path=/Packages/com.ruccho.unityped.manualgenerator from Package Manager.

Usage

  1. Create generator profile asset from Create > UniTyped > Manual Generator Profile in project view.
  2. Add generator item.
  3. Register csproj path of the project contains target scripts.
  4. Register output C# path. (will be overwritten!)
  5. Click Generate button.

image

Hint

  • Executable of manual generator UniTyped.Generator.Standalone.exe can be found in Packages/com.ruccho.unityped.manualgenerator/Editor/Executable~/netcoreapp3.1. You can use it from commandline with options --project=<CSPROJ PATH> --output=<OUTPUT SCRIPT PATH>.