using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("Rendering/MPB Color Setter")]
[ExecuteAlways, DisallowMultipleComponent]
public class MPBColorSetter : MonoBehaviour
{
[Header("Target Renderers (Reorderable)")]
[SerializeField] private Renderer[] targetRenderers;
[Header("Override Color")]
[ColorUsage(true, true)] [SerializeField]
private Color overrideColor = Color.white;
[Header("Shader Color Property (Auto Detect)")]
[SerializeField, Tooltip("비워두면 자동 탐색")]
private string colorPropertyName = string.Empty;
private static readonly Dictionary<string, int> s_IdCache = new();
private static readonly int s_FallbackId = Shader.PropertyToID("_BaseColor");
private static MaterialPropertyBlock s_Mpb;
[SerializeField, HideInInspector] private int colorPropertyID = -1;
private readonly Dictionary<Renderer, Color> originalColors = new();
public Color OverrideColor => overrideColor;
private void Awake()
{
RefreshCache();
ApplyCachedColor();
}
private void OnEnable() => ApplyCachedColor();
#if UNITY_EDITOR
private void OnValidate()
{
if (!Application.isPlaying)
{
if (colorPropertyID <= 0) RefreshCache();
ApplyCachedColor();
}
}
#endif
private void OnDestroy()
{
if (originalColors.Count == 0) return;
s_Mpb ??= new MaterialPropertyBlock();
foreach (var r in targetRenderers)
{
if (!r || !originalColors.TryGetValue(r, out var originalColor)) continue;
r.GetPropertyBlock(s_Mpb);
s_Mpb.SetColor(colorPropertyID, originalColor);
r.SetPropertyBlock(s_Mpb);
}
}
public void RefreshAndApply()
{
RefreshCache();
ApplyCachedColor();
}
public void ApplyCachedColor()
{
if (targetRenderers == null || targetRenderers.Length == 0) return;
if (colorPropertyID < 0) colorPropertyID = s_FallbackId;
s_Mpb ??= new MaterialPropertyBlock();
foreach (var r in targetRenderers)
{
if (!r) continue;
r.GetPropertyBlock(s_Mpb);
if (!originalColors.ContainsKey(r) && r.sharedMaterial.HasProperty(colorPropertyID))
{
originalColors[r] = r.sharedMaterial.GetColor(colorPropertyID);
}
s_Mpb.SetColor(colorPropertyID, overrideColor);
r.SetPropertyBlock(s_Mpb);
}
}
/// <summary>
/// 런타임에서 OverrideColor를 설정하고 즉시 적용하는 함수.
/// </summary>
/// <param name="newColor">새로 적용할 색상</param>
public void SetOverrideColor(Color newColor)
{
overrideColor = newColor;
ApplyCachedColor();
}
private void RefreshCache()
{
if (targetRenderers == null || targetRenderers.Length == 0)
targetRenderers = GetComponentsInChildren<Renderer>(true);
if (string.IsNullOrWhiteSpace(colorPropertyName))
colorPropertyName = DetectColorProperty(targetRenderers);
colorPropertyID = GetPropertyID(colorPropertyName);
}
private static int GetPropertyID(string name)
{
if (string.IsNullOrEmpty(name)) return s_FallbackId;
if (!s_IdCache.TryGetValue(name, out var id))
{
id = Shader.PropertyToID(name);
s_IdCache[name] = id;
}
return id;
}
private static string DetectColorProperty(Renderer[] rends)
{
string fallback = null;
foreach (var r in rends)
{
if (!r) continue;
foreach (var mat in r.sharedMaterials)
{
if (!mat || !mat.shader) continue;
#if UNITY_EDITOR
int cnt = UnityEditor.ShaderUtil.GetPropertyCount(mat.shader);
for (int i = 0; i < cnt; ++i)
{
if (UnityEditor.ShaderUtil.GetPropertyType(mat.shader, i) != UnityEditor.ShaderUtil.ShaderPropertyType.Color)
continue;
string name = UnityEditor.ShaderUtil.GetPropertyName(mat.shader, i);
if (name == "_BaseColor" || name == "_Color") return name;
fallback ??= name;
}
#else
foreach (var name in k_CommonColorProps)
if (mat.HasProperty(name)) return name;
#endif
}
}
return fallback ?? "_BaseColor";
}
#if !UNITY_EDITOR
private static readonly string[] k_CommonColorProps =
{ "_BaseColor", "_Color", "_Tint", "_Albedo" };
#endif
}
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(MPBColorSetter))]
public class MPBColorSetterEditor : Editor
{
private static readonly Color[] presetColors =
{
new Color(1f, 1f, 1f), // White
new Color(0f, 0f, 0f), // Black
new Color(1f, 0f, 0f), // Red
new Color(0f, 1f, 0f), // Green
new Color(0f, 0f, 1f), // Blue
new Color(1f, 1f, 0f), // Yellow
new Color(1f, 0f, 1f), // Magenta
new Color(0f, 1f, 1f), // Cyan
new Color(1f, 0.5f, 0f), // Orange
new Color(0.6f, 0.3f, 1f), // Purple
new Color(0.3f, 0.6f, 1f), // Sky Blue
new Color(0.2f, 0.8f, 0.4f), // Mint
new Color(0.5f, 0.2f, 0.2f), // Brown
new Color(0.2f, 0.2f, 0.2f), // Dark Gray
new Color(0.6f, 0.6f, 0.6f), // Light Gray
};
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawPropertiesExcluding(serializedObject, "m_Script");
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
EditorGUILayout.LabelField("🎨 Quick Color Presets", EditorStyles.boldLabel);
EditorGUILayout.Space(4);
EditorGUILayout.BeginVertical("box");
var setter = (MPBColorSetter)target;
var current = setter.OverrideColor;
int total = presetColors.Length;
int columnWidth = Mathf.Max(1, Mathf.FloorToInt(EditorGUIUtility.currentViewWidth / 50f));
int rowCount = Mathf.CeilToInt((float)total / columnWidth);
for (int row = 0; row < rowCount; row++)
{
EditorGUILayout.BeginHorizontal();
for (int col = 0; col < columnWidth; col++)
{
int i = row * columnWidth + col;
if (i >= total) break;
var color = presetColors[i];
var rect = GUILayoutUtility.GetRect(24, 24, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
// 테두리 처리
EditorGUI.DrawRect(rect, current == color ? Color.white : Color.gray);
Rect inner = new Rect(rect.x + 2, rect.y + 2, rect.width - 4, rect.height - 4);
EditorGUI.DrawRect(inner, color);
// 클릭 이벤트
if (Event.current.type == EventType.MouseDown && inner.Contains(Event.current.mousePosition))
{
Undo.RecordObject(setter, "Change Override Color");
var prop = serializedObject.FindProperty("overrideColor");
prop.colorValue = color;
serializedObject.ApplyModifiedProperties();
setter.RefreshAndApply();
GUI.changed = true;
}
// 툴팁 (마우스 오버 시 RGB 표시)
if (inner.Contains(Event.current.mousePosition))
{
GUI.Label(rect, new GUIContent("", $"R:{color.r:F2} G:{color.g:F2} B:{color.b:F2}"));
Repaint();
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
serializedObject.ApplyModifiedProperties();
}
}
위 컴포넌트를 붙이면 자동으로 Color Property 이름을 찾은 후, 사용할 수 있게 도와줍니다.
그 후 Color색상을 바꾸면 렌더러의 색상을 쉽게 바꿀 수 있습니다!
우측 이미지처럼 이제 색상을 바꿔주려고 귀찮게 메테리얼을 생성하고 할당해주고 바꿔줄 필요가 없다는 거죠!! WoW 진짜 최고!