From 98e1de269f050009291019fcbdff97eeafbab2f7 Mon Sep 17 00:00:00 2001
From: towneh <25694892+towneh@users.noreply.github.com>
Date: Wed, 25 Mar 2026 22:52:33 +0000
Subject: [PATCH] feat: Create BasisParameterDriver
Created a Parameter Driver component for use within a StateMachineBehaviour for the purpose of driving parameter changes when a state is entered.
---
.../Scripts/BasisParameterDriver.cs | 210 ++++++++++
.../Scripts/BasisParameterDriver.cs.meta | 2 +
.../Editor/BasisParameterDriverEditor.cs | 393 ++++++++++++++++++
.../Editor/BasisParameterDriverEditor.cs.meta | 2 +
4 files changed, 607 insertions(+)
create mode 100644 Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs
create mode 100644 Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs.meta
create mode 100644 Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs
create mode 100644 Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs.meta
diff --git a/Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs b/Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs
new file mode 100644
index 000000000..21087b24e
--- /dev/null
+++ b/Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+///
+/// Executes a list of parameter operations when the state is entered.
+///
+public class BasisParameterDriver : StateMachineBehaviour
+{
+ [Tooltip("When true, operations only run on the local instance (recommended for Add and Random).")]
+ public bool localOnly = true;
+
+ [Tooltip("Optional label shown in the editor for identification purposes.")]
+ public string debugString;
+
+ public Operation[] operations = Array.Empty();
+
+ // ------------------------------------------------------------------
+ // Data model
+ // ------------------------------------------------------------------
+
+ [Serializable]
+ public class Operation
+ {
+ public enum OperationType { Set, Add, Random, Copy }
+
+ public OperationType type = OperationType.Set;
+
+ [Tooltip("Animator parameter to write to.")]
+ public string destination;
+
+ // ---- Set / Add ----
+ public float value;
+
+ // ---- Random (float / int) ----
+ public float minValue;
+ public float maxValue = 1f;
+
+ // ---- Random (bool only) ----
+ [Range(0f, 1f), Tooltip("Probability that the bool is set to true.")]
+ public float chance = 0.5f;
+
+ // ---- Random (int only) ----
+ [Tooltip("Prevents the same integer value from being chosen twice in a row.")]
+ public bool preventRepeats;
+
+ // ---- Copy ----
+ [Tooltip("Animator parameter to read from.")]
+ public string source;
+
+ [Tooltip("Remap the source range to a different destination range.")]
+ public bool remapRange;
+ public float sourceMin = 0f;
+ public float sourceMax = 1f;
+ public float destMin = 0f;
+ public float destMax = 1f;
+ }
+
+ // ------------------------------------------------------------------
+ // Runtime state
+ // ------------------------------------------------------------------
+
+ // Tracks previous int values per parameter for preventRepeats.
+ private readonly Dictionary _lastIntValues = new Dictionary();
+
+ // ------------------------------------------------------------------
+ // StateMachineBehaviour callbacks
+ // ------------------------------------------------------------------
+
+ public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
+ {
+ foreach (Operation op in operations)
+ Execute(animator, op);
+ }
+
+ // ------------------------------------------------------------------
+ // Execution
+ // ------------------------------------------------------------------
+
+ private void Execute(Animator animator, Operation op)
+ {
+ if (string.IsNullOrEmpty(op.destination))
+ return;
+
+ AnimatorControllerParameterType destType = GetParamType(animator, op.destination);
+ if (destType == 0)
+ {
+ Debug.LogWarning($"[RNGParamDriver] Destination parameter '{op.destination}' not found on animator.", animator);
+ return;
+ }
+
+ switch (op.type)
+ {
+ case Operation.OperationType.Set:
+ WriteParam(animator, op.destination, destType, op.value);
+ break;
+
+ case Operation.OperationType.Add:
+ {
+ float current = ReadParamAsFloat(animator, op.destination, destType);
+ WriteParam(animator, op.destination, destType, current + op.value);
+ break;
+ }
+
+ case Operation.OperationType.Random:
+ ExecuteRandom(animator, op, destType);
+ break;
+
+ case Operation.OperationType.Copy:
+ ExecuteCopy(animator, op, destType);
+ break;
+ }
+ }
+
+ private void ExecuteRandom(Animator animator, Operation op, AnimatorControllerParameterType destType)
+ {
+ switch (destType)
+ {
+ case AnimatorControllerParameterType.Bool:
+ animator.SetBool(op.destination, UnityEngine.Random.value < op.chance);
+ break;
+
+ case AnimatorControllerParameterType.Int:
+ {
+ int min = Mathf.RoundToInt(op.minValue);
+ int max = Mathf.RoundToInt(op.maxValue);
+ int range = max - min + 1;
+ int result = UnityEngine.Random.Range(min, max + 1);
+
+ if (op.preventRepeats && range > 1 &&
+ _lastIntValues.TryGetValue(op.destination, out int last) && last == result)
+ {
+ result = min + ((result - min + 1) % range);
+ }
+
+ _lastIntValues[op.destination] = result;
+ animator.SetInteger(op.destination, result);
+ break;
+ }
+
+ default: // Float
+ animator.SetFloat(op.destination, UnityEngine.Random.Range(op.minValue, op.maxValue));
+ break;
+ }
+ }
+
+ private void ExecuteCopy(Animator animator, Operation op, AnimatorControllerParameterType destType)
+ {
+ if (string.IsNullOrEmpty(op.source))
+ return;
+
+ AnimatorControllerParameterType srcType = GetParamType(animator, op.source);
+ if (srcType == 0)
+ {
+ Debug.LogWarning($"[RNGParamDriver] Source parameter '{op.source}' not found on animator.", animator);
+ return;
+ }
+
+ float srcVal = ReadParamAsFloat(animator, op.source, srcType);
+
+ if (op.remapRange)
+ srcVal = Remap(srcVal, op.sourceMin, op.sourceMax, op.destMin, op.destMax);
+
+ WriteParam(animator, op.destination, destType, srcVal);
+ }
+
+ // ------------------------------------------------------------------
+ // Helpers
+ // ------------------------------------------------------------------
+
+ private static float ReadParamAsFloat(Animator animator, string name, AnimatorControllerParameterType type)
+ {
+ switch (type)
+ {
+ case AnimatorControllerParameterType.Float: return animator.GetFloat(name);
+ case AnimatorControllerParameterType.Int: return animator.GetInteger(name);
+ case AnimatorControllerParameterType.Bool: return animator.GetBool(name) ? 1f : 0f;
+ default: return 0f;
+ }
+ }
+
+ private static void WriteParam(Animator animator, string name, AnimatorControllerParameterType type, float value)
+ {
+ switch (type)
+ {
+ case AnimatorControllerParameterType.Float:
+ animator.SetFloat(name, value);
+ break;
+ case AnimatorControllerParameterType.Int:
+ animator.SetInteger(name, Mathf.RoundToInt(value));
+ break;
+ case AnimatorControllerParameterType.Bool:
+ animator.SetBool(name, value >= 0.5f);
+ break;
+ }
+ }
+
+ private static float Remap(float value, float inMin, float inMax, float outMin, float outMax)
+ {
+ if (Mathf.Approximately(inMin, inMax)) return outMin;
+ return Mathf.Lerp(outMin, outMax, Mathf.InverseLerp(inMin, inMax, value));
+ }
+
+ private static AnimatorControllerParameterType GetParamType(Animator animator, string name)
+ {
+ foreach (AnimatorControllerParameter p in animator.parameters)
+ if (p.name == name) return p.type;
+ return (AnimatorControllerParameterType)0;
+ }
+}
diff --git a/Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs.meta b/Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs.meta
new file mode 100644
index 000000000..0b027b18a
--- /dev/null
+++ b/Basis/Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 452585efa8cd90f48b5e9838afe0adf1
\ No newline at end of file
diff --git a/Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs b/Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs
new file mode 100644
index 000000000..92b5befbd
--- /dev/null
+++ b/Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs
@@ -0,0 +1,393 @@
+using System;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+using static BasisParameterDriver;
+using static BasisParameterDriver.Operation;
+
+[CustomEditor(typeof(BasisParameterDriver))]
+public class BasisParameterDriverEditor : Editor
+{
+ private readonly List _foldouts = new List();
+
+ // Type accent colours
+ private static readonly Color ColSet = new Color(0.22f, 0.46f, 0.76f);
+ private static readonly Color ColAdd = new Color(0.18f, 0.58f, 0.33f);
+ private static readonly Color ColRandom = new Color(0.76f, 0.46f, 0.10f);
+ private static readonly Color ColCopy = new Color(0.54f, 0.22f, 0.74f);
+
+ // SDK-sourced button colours
+ private static readonly Color ColButtonAdd = new Color(0.18f, 0.58f, 0.33f, 1f);
+ private static readonly Color ColButtonGray = new Color(0.5f, 0.5f, 0.5f, 1f);
+ private static readonly Color ColErrorRed = new Color(0.96f, 0.26f, 0.21f, 1f);
+
+ public override void OnInspectorGUI()
+ {
+ var driver = (BasisParameterDriver)target;
+ serializedObject.Update();
+
+ // ── Settings panel ────────────────────────────────────────────────
+ DrawPanel(new Color(0.25f, 0.25f, 0.30f, 0.5f), new Color(0.5f, 0.5f, 0.7f), () =>
+ {
+ EditorGUI.BeginChangeCheck();
+ bool localOnly = EditorGUILayout.Toggle(
+ new GUIContent("Local Only",
+ "When true, operations only run on the local instance (recommended for Add and Random)."),
+ driver.localOnly);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Local Only");
+ driver.localOnly = localOnly;
+ EditorUtility.SetDirty(driver);
+ }
+
+ EditorGUI.BeginChangeCheck();
+ string debugString = EditorGUILayout.TextField(
+ new GUIContent("Debug Label",
+ "Optional label shown in the editor for identification."),
+ driver.debugString ?? "");
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Debug Label");
+ driver.debugString = debugString;
+ EditorUtility.SetDirty(driver);
+ }
+ });
+
+ // ── Operations header ─────────────────────────────────────────────
+ if (driver.operations == null) driver.operations = Array.Empty();
+ DrawSectionLabel($"Operations ({driver.operations.Length})");
+
+ // Sync foldout list
+ while (_foldouts.Count < driver.operations.Length) _foldouts.Add(true);
+ while (_foldouts.Count > driver.operations.Length) _foldouts.RemoveAt(_foldouts.Count - 1);
+
+ // ── Operations list ───────────────────────────────────────────────
+ if (driver.operations.Length == 0)
+ {
+ GUIStyle emptyStyle = new GUIStyle(EditorStyles.miniLabel)
+ {
+ fontStyle = FontStyle.Bold,
+ wordWrap = true
+ };
+ emptyStyle.normal.textColor = new Color(0.6f, 0.6f, 0.6f);
+ GUILayout.Label("No operations — click '+ Add Operation' below.", emptyStyle);
+ GUILayout.Space(4);
+ }
+
+ int removeIndex = -1;
+ int swapA = -1, swapB = -1;
+
+ for (int i = 0; i < driver.operations.Length; i++)
+ DrawOperationCard(driver, i, ref removeIndex, ref swapA, ref swapB);
+
+ // Apply deferred mutations
+ if (swapA >= 0)
+ {
+ Undo.RecordObject(driver, "Reorder Operation");
+ (driver.operations[swapA], driver.operations[swapB]) = (driver.operations[swapB], driver.operations[swapA]);
+ EditorUtility.SetDirty(driver);
+ }
+ if (removeIndex >= 0)
+ {
+ Undo.RecordObject(driver, "Remove Operation");
+ var list = new List(driver.operations);
+ list.RemoveAt(removeIndex);
+ driver.operations = list.ToArray();
+ EditorUtility.SetDirty(driver);
+ }
+
+ // ── Add button ────────────────────────────────────────────────────
+ GUILayout.Space(4);
+ DrawSdkButton("+ Add Operation", ColButtonAdd, 30, () =>
+ {
+ Undo.RecordObject(driver, "Add Operation");
+ var list = new List(driver.operations) { new Operation() };
+ driver.operations = list.ToArray();
+ _foldouts.Add(true);
+ EditorUtility.SetDirty(driver);
+ });
+
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ // ─────────────────────────────────────────────────────────────────────
+ // Operation card
+ // ─────────────────────────────────────────────────────────────────────
+
+ private void DrawOperationCard(BasisParameterDriver driver, int i,
+ ref int removeIndex, ref int swapA, ref int swapB)
+ {
+ var op = driver.operations[i];
+ Color accent = TypeAccent(op.type);
+ Color bg = new Color(accent.r * 0.15f, accent.g * 0.15f, accent.b * 0.15f, 0.5f);
+ Color dimBorder = new Color(accent.r * 0.5f, accent.g * 0.5f, accent.b * 0.5f);
+
+ // Card background — mimics SDK panel style (tinted bg, 2px border, strong accent bottom)
+ Rect cardRect = EditorGUILayout.BeginVertical();
+ if (Event.current.type == EventType.Repaint)
+ {
+ EditorGUI.DrawRect(cardRect, dimBorder); // outer border
+ EditorGUI.DrawRect(new Rect(cardRect.x + 2, cardRect.y + 2, cardRect.width - 4, cardRect.height - 4), bg); // inner bg
+ EditorGUI.DrawRect(new Rect(cardRect.x, cardRect.y, 2, cardRect.height), accent); // left bar
+ EditorGUI.DrawRect(new Rect(cardRect.x, cardRect.yMax - 3, cardRect.width, 3), accent); // bottom accent
+ }
+
+ GUILayout.Space(5);
+
+ // ── Header row ────────────────────────────────────────────────────
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Space(8);
+
+ // Foldout
+ GUIStyle foldStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold, fontSize = 12 };
+ string destLabel = string.IsNullOrEmpty(op.destination) ? "" : op.destination;
+ _foldouts[i] = EditorGUILayout.Foldout(_foldouts[i], $" [{i}] {destLabel}", true, foldStyle);
+
+ // Type pill — SDK button style: bold white text, coloured bg, radius via miniButton skin
+ GUIStyle pillStyle = new GUIStyle(EditorStyles.miniButton)
+ {
+ fontStyle = FontStyle.Bold,
+ fontSize = 10,
+ alignment = TextAnchor.MiddleCenter
+ };
+ pillStyle.normal.textColor = Color.white;
+ pillStyle.hover.textColor = Color.white;
+ Color prevBg = GUI.backgroundColor;
+ GUI.backgroundColor = accent;
+ GUILayout.Button(op.type.ToString().ToUpper(), pillStyle, GUILayout.Width(58), GUILayout.Height(16));
+ GUI.backgroundColor = prevBg;
+
+ // ▲ ▼ ✕ — SDK icon button style: bold white text, coloured bg
+ GUIStyle iconStyle = MakeIconButtonStyle();
+
+ GUI.enabled = i > 0;
+ GUI.backgroundColor = ColButtonGray;
+ if (GUILayout.Button("▲", iconStyle, GUILayout.Width(22), GUILayout.Height(18))) { swapA = i - 1; swapB = i; }
+
+ GUI.enabled = i < driver.operations.Length - 1;
+ GUI.backgroundColor = ColButtonGray;
+ if (GUILayout.Button("▼", iconStyle, GUILayout.Width(22), GUILayout.Height(18))) { swapA = i; swapB = i + 1; }
+
+ GUI.enabled = true;
+ GUI.backgroundColor = ColErrorRed;
+ if (GUILayout.Button("✕", iconStyle, GUILayout.Width(22), GUILayout.Height(18))) removeIndex = i;
+ GUI.backgroundColor = prevBg;
+
+ EditorGUILayout.EndHorizontal();
+
+ // ── Body ──────────────────────────────────────────────────────────
+ if (_foldouts[i])
+ {
+ GUILayout.Space(2);
+ EditorGUI.indentLevel += 2;
+
+ // Type
+ EditorGUI.BeginChangeCheck();
+ var newType = (OperationType)EditorGUILayout.EnumPopup("Type", op.type);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Operation Type");
+ driver.operations[i].type = newType;
+ EditorUtility.SetDirty(driver);
+ }
+
+ // Destination
+ EditorGUI.BeginChangeCheck();
+ string newDest = EditorGUILayout.TextField("Destination", op.destination ?? "");
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Destination");
+ driver.operations[i].destination = newDest;
+ EditorUtility.SetDirty(driver);
+ }
+
+ // Type-specific fields
+ switch (op.type)
+ {
+ case OperationType.Set:
+ case OperationType.Add:
+ EditorGUI.BeginChangeCheck();
+ float val = EditorGUILayout.FloatField("Value", op.value);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Value");
+ driver.operations[i].value = val;
+ EditorUtility.SetDirty(driver);
+ }
+ break;
+
+ case OperationType.Random:
+ EditorGUI.BeginChangeCheck();
+ float minV = EditorGUILayout.FloatField("Min Value", op.minValue);
+ float maxV = EditorGUILayout.FloatField("Max Value", op.maxValue);
+ float chance = EditorGUILayout.Slider(
+ new GUIContent("Chance (bool)", "Probability the bool is set to true."),
+ op.chance, 0f, 1f);
+ bool noRepeat = EditorGUILayout.Toggle(
+ new GUIContent("Prevent Repeats (int)", "Prevents the same int from being chosen twice in a row."),
+ op.preventRepeats);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Random Operation");
+ driver.operations[i].minValue = minV;
+ driver.operations[i].maxValue = maxV;
+ driver.operations[i].chance = chance;
+ driver.operations[i].preventRepeats = noRepeat;
+ EditorUtility.SetDirty(driver);
+ }
+ break;
+
+ case OperationType.Copy:
+ EditorGUI.BeginChangeCheck();
+ string src = EditorGUILayout.TextField(
+ new GUIContent("Source", "Animator parameter to read from."),
+ op.source ?? "");
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Source");
+ driver.operations[i].source = src;
+ EditorUtility.SetDirty(driver);
+ }
+
+ DrawThinSeparator(new Color(accent.r * 0.4f, accent.g * 0.4f, accent.b * 0.4f));
+
+ EditorGUI.BeginChangeCheck();
+ bool remap = EditorGUILayout.Toggle(
+ new GUIContent("Remap Range", "Remap the source range to a different destination range."),
+ op.remapRange);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Toggle Remap Range");
+ driver.operations[i].remapRange = remap;
+ EditorUtility.SetDirty(driver);
+ }
+
+ if (op.remapRange)
+ {
+ EditorGUI.indentLevel++;
+ EditorGUI.BeginChangeCheck();
+ float srcMin = EditorGUILayout.FloatField("Source Min", op.sourceMin);
+ float srcMax = EditorGUILayout.FloatField("Source Max", op.sourceMax);
+ float dstMin = EditorGUILayout.FloatField("Dest Min", op.destMin);
+ float dstMax = EditorGUILayout.FloatField("Dest Max", op.destMax);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(driver, "Change Remap Range");
+ driver.operations[i].sourceMin = srcMin;
+ driver.operations[i].sourceMax = srcMax;
+ driver.operations[i].destMin = dstMin;
+ driver.operations[i].destMax = dstMax;
+ EditorUtility.SetDirty(driver);
+ }
+ EditorGUI.indentLevel--;
+ }
+ break;
+ }
+
+ EditorGUI.indentLevel -= 2;
+ GUILayout.Space(5);
+ }
+ else
+ {
+ GUILayout.Space(3);
+ }
+
+ EditorGUILayout.EndVertical();
+ GUILayout.Space(3);
+ }
+
+ // ─────────────────────────────────────────────────────────────────────
+ // SDK-style IMGUI helpers
+ // ─────────────────────────────────────────────────────────────────────
+
+ ///
+ /// Full-width button matching SDK DocumentationButton / AutoFixButton:
+ /// bold white text 14pt, coloured bg, centered.
+ ///
+ private static void DrawSdkButton(string text, Color bgColor, float height, Action onClick)
+ {
+ GUIStyle style = new GUIStyle(GUI.skin.button)
+ {
+ fontStyle = FontStyle.Bold,
+ fontSize = 14,
+ alignment = TextAnchor.MiddleCenter
+ };
+ style.normal.textColor = Color.white;
+ style.hover.textColor = Color.white;
+ style.active.textColor = Color.white;
+ style.focused.textColor = Color.white;
+
+ Color prev = GUI.backgroundColor;
+ GUI.backgroundColor = bgColor;
+ if (GUILayout.Button(text, style, GUILayout.Height(height)))
+ onClick?.Invoke();
+ GUI.backgroundColor = prev;
+ GUILayout.Space(4);
+ }
+
+ ///
+ /// Draws content inside a styled panel matching SDK CreateErrorPanel / CreatePassedPanel:
+ /// tinted bg, 2px dim border all sides, 3px strong accent on bottom.
+ ///
+ private static void DrawPanel(Color bgColor, Color borderAccent, Action content)
+ {
+ Color dimBorder = new Color(borderAccent.r * 0.5f, borderAccent.g * 0.5f, borderAccent.b * 0.5f);
+
+ Rect panelRect = EditorGUILayout.BeginVertical();
+ if (Event.current.type == EventType.Repaint)
+ {
+ EditorGUI.DrawRect(panelRect, dimBorder);
+ EditorGUI.DrawRect(new Rect(panelRect.x + 2, panelRect.y + 2, panelRect.width - 4, panelRect.height - 4), bgColor);
+ EditorGUI.DrawRect(new Rect(panelRect.x, panelRect.yMax - 3, panelRect.width, 3), borderAccent);
+ }
+
+ GUILayout.Space(5);
+ EditorGUI.indentLevel++;
+ content?.Invoke();
+ EditorGUI.indentLevel--;
+ GUILayout.Space(8);
+ EditorGUILayout.EndVertical();
+ GUILayout.Space(6);
+ }
+
+ private static void DrawSectionLabel(string text)
+ {
+ GUIStyle style = new GUIStyle(EditorStyles.boldLabel) { fontSize = 13 };
+ style.normal.textColor = Color.white;
+ GUILayout.Space(2);
+ GUILayout.Label(text, style);
+ GUILayout.Space(2);
+ }
+
+ private static void DrawThinSeparator(Color color)
+ {
+ Rect r = EditorGUILayout.GetControlRect(false, 1f);
+ EditorGUI.DrawRect(r, color);
+ GUILayout.Space(2);
+ }
+
+ private static GUIStyle MakeIconButtonStyle()
+ {
+ var style = new GUIStyle(GUI.skin.button)
+ {
+ fontStyle = FontStyle.Bold,
+ fontSize = 11,
+ alignment = TextAnchor.MiddleCenter
+ };
+ style.normal.textColor = Color.white;
+ style.hover.textColor = Color.white;
+ style.active.textColor = Color.white;
+ style.focused.textColor = Color.white;
+ return style;
+ }
+
+ private static Color TypeAccent(OperationType t) => t switch
+ {
+ OperationType.Set => ColSet,
+ OperationType.Add => ColAdd,
+ OperationType.Random => ColRandom,
+ OperationType.Copy => ColCopy,
+ _ => new Color(0.45f, 0.45f, 0.45f),
+ };
+}
diff --git a/Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs.meta b/Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs.meta
new file mode 100644
index 000000000..66fb79d49
--- /dev/null
+++ b/Basis/Packages/com.basis.sdk/Scripts/Editor/BasisParameterDriverEditor.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: e791397e8c7c6824490842bdd84d7250
\ No newline at end of file