- Home /
How to create shared color channels
The plan is to have a global list of colors that can be referenced by other objects.
I would like it to function where I can can assign multiple objects the same color in the inspector (for example in their sprite renderer color field), and then when I change this color via script, all objects which use this color will also change.
I was previously told I could use the color swatches to have objects share colors, but I then learned it was not possible to access the swatches with a script and edit the color values.
Is this even possible?! Cuz I'm struggling to find anything that can help me, even though I feel like this should be something simple to implement.
Answer by Hellium · May 24, 2020 at 08:39 PM
Unfortunately, what you want to achieve is not possible "out of the box". The Color
type is a struct (value-type), not a class (reference type), so each instance of this struct is independent from the others.
You will have to wrap the struct around a class and make sure your objects manipulate the same instance of the class.
Here is my solution using Scriptable Objects. I have tested it and works fine. You might need to adapt the code a little bit to fit your needs.
ColorReference.cs
This is the wrapper of the Color
struct. You can create ScriptableObjects from the Assets/Create
menu, or by clicking the +
/ Create
in your Project
tab, or by right clicking in your Project
tab
using System;
using UnityEngine;
[CreateAssetMenu]
public class ColorReference : ScriptableObject
{
[SerializeField] private UnityEngine.Color color;
public float a
{
get => color.a;
set => color.a = value;
}
public float r
{
get => color.r;
set => color.r = value;
}
public float g
{
get => color.g;
set => color.g = value;
}
public float b
{
get => color.b;
set => color.b = value;
}
// Define every other function / property you need
public void Set(UnityEngine.Color color)
{
this.color = color;
Changed?.Invoke();
}
public event Action Changed;
public static implicit operator UnityEngine.Color(ColorReference color)
=> color.color;
public static implicit operator ColorReference(UnityEngine.Color color)
=> new ColorReference() { color = color };
}
ColorReferenceEditor.cs in an Editor
folder
Custom editor for the ColorReference scriptable object used to invoke the Change event when the color is changed from the scriptable object
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(ColorReference))]
public class ColorReferenceEditor : Editor
{
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
if(EditorGUI.EndChangeCheck())
{
( target as ColorReference ).Set(( target as ColorReference ));
}
}
}
ColorAssigner.cs
Base class of the concrete ColorAssigners. The ColorAssigners have the responsibility to react to the change of the color
using UnityEngine;
[ExecuteInEditMode]
public abstract class ColorAssigner : MonoBehaviour
{
[SerializeField] private ColorReference color;
public ColorReference ColorReference
{
get => color;
set
{
if (color == value)
return;
ColorReference oldValue = color;
if (oldValue != null && value == null)
UnsubscribeColorChange();
color = value;
if(oldValue == null && value != null)
SubscribeColorChange();
}
}
protected abstract void AssignColor(Color color);
private void SubscribeColorChange() => color.Changed += OnColorChanged;
private void UnsubscribeColorChange() => color.Changed -= OnColorChanged;
private void OnColorChanged() => AssignColor(color);
private void Start()
{
if (color != null)
{
UnsubscribeColorChange();
SubscribeColorChange();
AssignColor(color);
}
}
private void OnDestroy()
{
if (color != null)
UnsubscribeColorChange();
}
#if UNITY_EDITOR
private void OnValidate()
{
if (color != null)
{
UnsubscribeColorChange();
SubscribeColorChange();
OnColorChanged();
}
}
#endif
}
GraphicColorAssigner.cs
This class is a concrete implementation of the ColorAssigner abstract class to change the color of any UI element (Text Mesh Pro included) when the color of the ScriptableObject changes
using UnityEngine;
using UnityEngine.UI;
public class GraphicColorAssigner : ColorAssigner
{
protected override void AssignColor(Color color)
{
GetComponent<Graphic>().color = color;
}
}
RendererColorAssigner.cs
This class is a concrete implementation of the ColorAssigner abstract class to change the color of the Renderer component (MeshRenderer / SpriteRenderer) when the color of the ScriptableObject changes
using UnityEngine;
public class RendererColorAssigner : ColorAssigner
{
protected override void AssignColor(Color color)
{
GetComponent<Renderer>().sharedMaterial.color = color;
}
}
ColorReferencePropertyDrawer.cs in an Editor
folder
If you want to directly change the color from the ColorAssigners' inspector, place this script in an Editor folder
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(ColorReference))]
public class ColorReferencePropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
DrawLabel(position, label);
position.xMin += EditorGUIUtility.labelWidth;
DrawScriptableObjectField(position, property);
if(property.objectReferenceValue)
DrawColorField(position, property);
}
private static void DrawLabel(Rect position, GUIContent label)
{
GUI.Label(position, label);
}
private void DrawScriptableObjectField(Rect position, SerializedProperty property)
{
if (property.objectReferenceValue != null)
position.xMin = position.xMax - 20;
EditorGUI.ObjectField(position, property, typeof(ColorReference), GUIContent.none);
}
private void DrawColorField(Rect position, SerializedProperty property)
{
SerializedObject colorReference = new SerializedObject(property.objectReferenceValue);
SerializedProperty colorProperty = colorReference.FindProperty("color");
EditorGUI.BeginChangeCheck();
position.xMax -= 20;
Color color = EditorGUI.ColorField(position, GUIContent.none, colorProperty.colorValue);
if(EditorGUI.EndChangeCheck())
{
( colorReference.targetObject as ColorReference ).Set(color);
property.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}
}
I have tested the solution with the following simple script
// Drag & drop the scriptable object in the inspector
public ColorReference colorRef;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
colorRef.Set(Color.HSVToRGB(Random.Range(0, 1f), 1, 1));
}
Wow, thanks for such a thorough response!
I am kind of unclear on how to use these scripts though. I've added them, but nothing in the editor seems to change and I don't know which scripts I attach to which objects.
Could I get a bit of an explanation, maybe an example, of how I would go about assigning a color to a single object, and how I would change it's color in a script?
Thanks.
Attach the
RendererColorAssigner
/GraphicColorAssigner
component to your objects.Create a
ColorReference
using the Assets/Create menuSelect the scriptable object from the inspector of the
RendererColorAssigner
/GraphicColorAssigner
componentsChange the color in the inspector of one of the components, the color of all the objects with the
RendererColorAssigner
/GraphicColorAssigner
component should change
thanks. there is an issue tho that the color doesn't seem to change during play when i change it with a script. Additionally, when changing the color in the inspector, I get a console error saying "The object of type 'RendererColorAssigner' has been destroyed but you are still trying to access it."
Edit: I'm thinking it has to do with fact that ScriptableObjects can only be used in the editor not during runtime. which is a huge problem as modifying the colors during runtime is half of what i'm trying to do
Your answer
Follow this Question
Related Questions
Change startcolor of particle system through script 1 Answer
Instantiating multiple sprites and assigning different colors for each 1 Answer
How to change the color from code 1 Answer
Multiple Cars not working 1 Answer
When a ball is clicked and background color matches it's color I get a point? 0 Answers