/uPalette

Centralized management & batch change system of colors for Unity.

Primary LanguageC#MIT LicenseMIT

uPalette

license license license

Docs (English, 日本語) | Demo

Centrally manage colors and text styles in Unity projects.

Demo

Table of Contents

Details

Concept & Features

In typical application development, one color is applied to multiple locations. The same blue color is applied to the button background, icon color, and outline in the following example.

Apply blue color

Now think about changing this color from blue to green. In Unity, color values are serialized in Prefabs and Scenes, so we need to change all these values one by one.

Change the color to green

This workload increases with the size of the project. With uPalette, such changes can be applied in batches by centralizing color management.

uPalette

uPalette also allows you to manage text styles and gradients as well as colors.

Character Styles & Gradients

In addition, the theme feature allows you to save a set of colors and text styles as a theme. By switching the active theme, you can reflect the colors and text style according to that theme.

Theme Feature

Setup

Requirements

Unity2020.1 or higher.

Installation

インストールは以下の手順で行います。

  1. Select Window > Package Manager in Unity Editor.
  2. Select “+” Button > Add package from git URL .
  3. Enter the following URL.

Install

If you want to install a specific version, add the version to the end of the URL as follows.

You can also update the version in the same manner as installation.

Note that if you get a message like No 'git' executable was found. Please install Git on your system and restart Unity, you need to set up Git on your machine.

Getting Started

Create Palette Store

To use uPalette, first open the Palette Editor from Window > uPalette > Palette Editor. You will see a window like as follows when you open the Palette Editor.

Palette Editor

Next, create a Palette Store asset by clicking the center Create Palette Store button. Palette Store is the asset that will hold the data for the uPalette. You can place this anywhere in the project, but not in the Editor folder or Streaming Assets folder because this asset is used at runtime.

After creating a Palette Store asset, the Palette Editor will change to the following display.

Palette Editor

Create Entry

In uPalette, color and character style settings are called Entries. You can add an Entry by pressing the “+” button in the upper right corner of the Palette Editor.

Add Entry

You can rename an Entry by clicking on its name. Entries can also be deleted by right-clicking menu of them.

Rename & Remove Entry

You can also drag elements to reorder them.

Apply Entry

To apply the color and character style you created to components, select the target GameObject and press the Apply button for the target entry. The names of applicable components and properties will then be listed, and you can select the one you want to apply.

Apply Entry

This will synchronize the entries and properties. When the value of a synchronized Entry changed, the property is automatically rewritten.

Change Entry Value

At this time, a component named Synchronizer is attached to the target GameObject. You can change the target Entry from this Inspector. If you want to remove synchronization with the Entry, detach this component.

Synchroizer

Note that When the Entry is applied to Prefab, It is not serialized in Prefab as in the normal Prefab workflow. You can serialize by right-clicking menu of Prefab.

Serialization

Highlight synchronized GameObjects

Select Highlight from the Entry’s right-clicking menu to highlight (select) the synchronized GameObjects.

Highlight

Handling non-color Entries.

Up to this point, I have described how to manage colors in uPalette. In addition to colors, there are other palette types in uPalette, such as gradients and character styles. You can switch between Palette types from the drop-down menu in the upper left corner of the Palette Editor.

Various Palettes

Each drop-down menu is described below.

Name Description
Color Use to manage color.
Gradient Use to manage gradient.
Character Style Use to manage character style of uGUI Text.
Character Style TMP Use to manage character style of Text Mesh Pro.

Organizing Entries into Folders

You can organize entries into folders by separating entry names with slashes. When entries are organized into folders, they are displayed hierarchically in the Palette Editor as shown in the figure below.

Folder Mode

The setting to organize entries into folders can be changed from the following menu.
  • Project Settings > uPalette > Use Folder View in Palette Editor

If you choose not to organize entries into folders, all elements will be displayed flatly and can be rearranged via drag and drop. If you choose to organize entries into folders, they will be sorted in alphabetical order.

Theme Feature Usage

What is Theme?

The Theme feature allows you to save a set of the Entries as “theme”. You can save multiple Themes, and switch between them to reflect the colors and character styles of each Theme.

Theme

Create Theme

To create a Theme, open the Theme Editor from Window > uPalette > Theme Editor. By default, there is a Theme named Default, and you can create a new Theme from the upper left “+” button. You can rename, delete, reorder, etc. using the same operations as in the Entry Editor.

Theme Editor

When a Theme is added, a column is added to the Palette Editor for setting Entries for that theme. By editing this column, you can set values according to the Theme.

Palette Editor

You can set the Theme for each Palette type. The palette type can be changed from the drop-down menu in the upper left corner of the Theme Editor.

Change Palette Type

Switch Themes (Editor)

You can switch Themes by pressing the Activate button from the Theme Editor. When you switch themes, the values of the Entry in that Theme are immediately reflected.

Change Theme

Switch Themes (Script)

To switch Themes at runtime, use SetActiveTheme() in the Palette class. Below is an example script that switches the ColorPalette theme using automatically generated theme enum.

using System;
using UnityEngine;
using uPalette.Generated;
using uPalette.Runtime.Core;

public class Example : MonoBehaviour
{
    public void OnGUI()
    {
        foreach (ColorTheme colorTheme in Enum.GetValues(typeof(ColorTheme)))
            if (GUILayout.Button(colorTheme.ToString()))
            {
                var colorPalette = PaletteStore.Instance.ColorPalette;
                colorPalette.SetActiveTheme(colorTheme.ToThemeId());
            }
    }
}

Attach this to GameObject and play to switch Themes as follows.

Change Theme

Advanced Usage

SynchronizeEvent - Only notify changes in entry values

As mentioned above, the Synchronizer component applies the value to the target property when the Entry value is changed. In contrast, you can receive only value change notifications as event by using the following Synchronize Event components.

  • Color Synchronize Event
  • Gradient Synchronize Event
  • Character Style Synchronize Event
  • Character Style TMP Synchronize Event

To use it, attach the above component and setup UnityEvent to handle when the value changes.

Change Theme

Automatic generation of enums for Entries and Themes

When working with uPalette from script, it is useful to have a script automatically generated to access Theme and Entry information.

If you set Project Settings > uPalette > Name Enums File GenerationtoWhen Window Loses Focus, this file will be automatically generated when the focus is lost from the Palette Editor or Theme Editor. If you set a folder to Name Enums File Location, the file will be generated in that folder. If not set, the file will be generated in the Assets folder.

Project Settings

The following enum is generated.

using System;

namespace uPalette.Generated
{
    public enum ColorEntry
    {
        Red,
        Green,
        Blue,
    }
}

ToEntryId(), defined as extension method of this enum, can be used to get the Entry ID.

using uPalette.Generated;

public class Example
{
    private void Foo()
    {
        ColorEntry.Red.ToEntryId();
    }
}

The same can be used for other types of Entries and Themes.

If you check the Contains Folder Name to Name Enums option in the project settings, the folder name will also be included in the Enums. If you uncheck this option, the name used for the Enums will exclude the folder name.

Get / Monitor entry value from script

If you want to get or monitor the entry value from script, you can use the GetActiveValue() method of each palette. IReadOnlyObservableProperty<T> will be returned, so use its Value property to get the current value. And you can also use the Subscribe() to monitor changes in value if you want to watch for changes like theme changes.

using System;
using UnityEngine;
using uPalette.Generated;
using uPalette.Runtime.Core;

public class Example : MonoBehaviour
{
    private void Start()
    {
        // Get the color palette.
        var colorPalette = PaletteStore.Instance.ColorPalette;

        // Get the color entry id from the auto-generated ColorEntry enum.
        var targetColorEntryId = ColorEntry.KeyColor1.ToEntryId();

        var colorProperty = colorPalette.GetActiveValue(targetColorEntryId);

        // If you want to get the current value, use the Value property.
        var targetValue = colorProperty.Value;

        // If you want to get the value when the theme is changed, subscribe the property.
        IObserver<Color> observer;
        var disposable = colorProperty.Subscribe(observer);
    }
}

Don't manage Palette Data with PreloadedAssets

In default, Palette Data is registered to PreloadedAssets, and it is loaded automatically in runtime. This means that the Palette Data is included in the application.

If you don't want to register Palette Data to PreloadedAssets, for example, when you want to make it to the AssetBundle, do the following.

  • Uncheck Project Settings > uPalette > Automatic Runtime Data Loading.

Automatic Runtime Data Loading

If you do this, the Palette Data is removed from PreloadedAssets. So you need to load the PaletteStore manually. The loaded PaletteStore is automatically registered to PaletteStore.Instance and you can access it from there. Note that you need to load it before loading GUIs that use uPalette.

// You must load the PaletteStore manually before loading GUIs that use uPalette.
var _ = Resources.Load<PaletteStore>("PaletteStore");

In editor, the PaletteStore is always loaded via AssetDatabase.

Edit uPalette data from scripts

You can edit uPalette data from scripts by retrieving a palette from the PaletteStore as shown below. Note that you must always set the dirty flag after editing the PaletteStore, because it is a ScriptableObject.

// Get PaletteStore.
var paletteStore = PaletteStore.Instance;

// Get each palette.
var colorPalette = PaletteStore.Instance.ColorPalette;
var gradientPalette = PaletteStore.Instance.GradientPalette;
var characterStylePalette = PaletteStore.Instance.CharacterStylePalette;
var characterStyleTMPPalette = PaletteStore.Instance.CharacterStyleTMPPalette;

// Set the dirty flag after editing.
EditorUtility.SetDirty(paletteStore);

// Save assets if you need.
AssetDatabase.SaveAssets();

Reflects values to your own components

uPalette includes Synchronizers to reflect values to standard component.

You can also create a Synchronizer to reflect values in your own components. As an example, consider a custom component with Gradient as a property.

using UnityEngine;

public class SampleGradient : MonoBehaviour
{
    [SerializeField] private Gradient _gradient;

    public Gradient Gradient
    {
        get => _gradient;
        set => _gradient = value;
    }
}

A Synchronizer to reflect the value to this property can be created as follows.

using UnityEngine;
using uPalette.Runtime.Core.Synchronizer.Gradient;

[AddComponentMenu("")]
[DisallowMultipleComponent]
[RequireComponent(typeof(SampleGradient))]
[GradientSynchronizer(typeof(SampleGradient), "Gradient")]
public sealed class GraphicColorSynchronizer : GradientSynchronizer<SampleGradient>
{
    protected override Gradient GetValue()
    {
        return _component.Gradient;
    }

    protected override void SetValue(Gradient value)
    {
        _component.Gradient = value;
    }
}

Configure behavior when an entry is not found

When a target Entry is not found, you may want to output error logs, or you may want to ignore it. You can configure the behavior when an Entry is not found by Project Settings > uPalette > Missing Entry Error.

Missing Entry Error

The selections are as follows.

Name Description
None Do nothing.
Warning Output warning logs.
Error Output error logs.
Exception Throw exceptions.

Implemented Synchronizers

The Synchronizer implemented in uPalette is as follows.

Entry Type Target Class Name Target Property Name
Color UnityEngine.UI.Graphic color
Color UnityEngine.UI.Outline effectColor
Color UnityEngine.UI.Selectable colors.normalColor
Color UnityEngine.UI.Selectable colors.selectedColor
Color UnityEngine.UI.Selectable colors.pressedColor
Color UnityEngine.UI.Selectable colors.disabledColor
Color UnityEngine.UI.Selectable colors.highlightedColor
Color UnityEngine.UI.InputField caretColor
Color UnityEngine.UI.InputField selectionColor
Color TMPro.TMP_InputField caretColor
Color TMPro.TMP_InputField selectionColor
CharacterStyle UnityEngine.UI.Text font / fontStyle / fontSize / lineSpacing
CharacterStyleTMP TMPro.TextMeshProUGUI font / fontStyle / fontSize / enableAutoSizing / characterSpacing / wordSpacing / lineSpacing / paragraphSpacing

Technical details

About the timing of reflecting Entries

In Unity, information such as colors and character styles set for each component are serialized as values as they are. Therefore, these serialized values should be rewritten when they are changed.

However, this would result in changes to many Scenes and Prefabs when an Entry is changed. So uPalette reflects the Entry according to the following rules.

  • Entries are serialized as IDs, not values.
  • In Edit Mode, this Entry is reflected and observed for changes when OnEnable is called.
  • In Play Mode, Entries are reflected when Start() is called.

In addition, to prevent changes when a Scene is opened in Edit Mode, the dirty flags is not set when an Entry are reflected.

How to update from version 1

In upgrading uPalette from version 1 to version 2, I have made significant changes to the data structure and data placement.

If you have been using version 1, you can transfer your data to version 2 by pressing the following button from Project Settings before creating Palette Store.

How to update

After you click on the button, the panel to save the Palette Store will appear. You can place the Palette Store anywhere in the project, but not in the Editor folder or Streaming Assets folder because this asset is used at runtime.

Demo

  1. Clone this repository.
  2. Open and play the following scene.

Licenses

This software is released under the MIT license. You are free to use it within the scope of the license, but the following copyright and license notices are required.

In addition, the table of contents for this document has been created using the following software

See Third Party Notices.md for more information about the license of toc-generator.