Toggle and OnValueChanged with inverse dynamic value ?

With a toggle we can use a unityEvent to call function with dynamic parameters, like if toggle is ON then otherThing.gameObject.setActive() is also ON.
Very usefull to save script use, but can it be inversed?
I would like : if toggle is ON, then other gameObject is OFF.

1 Like

I think this is an elegant solution:
Attach this component to your Toggle gameobject and check out the Inspector:

using UnityEngine.EventSystems;
using UnityEngine;
using UnityEngine.UI;

public class InvertedToggleEvent : MonoBehaviour{

    //wont show in inspector
    //public    UnityEngine.Events.UnityEvent<bool> toggleEvent;
 
    //this will show in inspector
    [System.Serializable]
    public class UnityEventBool : UnityEngine.Events.UnityEvent<bool>{}
    public    UnityEventBool onValueChangedInverse;
 

    private void Start()
    {
        GetComponent<Toggle>().onValueChanged.AddListener( (on)=>{ onValueChangedInverse.Invoke(!on); } );
    }
}
3 Likes

How to do that without a script?

I believe nothing has changed since then, so you don’t.

Editor script for this issue. Generated with AI

using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using UnityEditor.UI;

[CustomEditor(typeof(Toggle), true)]
[CanEditMultipleObjects]  
public class ToggleEditorExtended : ToggleEditor
{
    public override void OnInspectorGUI()
    {
        // Display standard Toggle inspector
        base.OnInspectorGUI();
        
        // Check if ToggleExtended component exists
        Toggle toggle = target as Toggle;
        if (toggle == null) return;
        
        // Ensure ToggleExtended component always exists
        ToggleExtended toggleExtended = EnsureToggleExtended(toggle);
        
        // Always display the inverted event section
        EditorGUILayout.Space(10);
        EditorGUILayout.LabelField("Extended Events", EditorStyles.boldLabel);
        
        if (toggleExtended != null)
        {
            SerializedObject extendedObject = new SerializedObject(toggleExtended);
            extendedObject.Update();
            
            SerializedProperty invertedEventProp = extendedObject.FindProperty("m_OnValueChangedInverted");
            if (invertedEventProp != null)
            {
                EditorGUILayout.PropertyField(invertedEventProp, 
                    new GUIContent("On Value Changed (Inverted)", 
                    "Event called with inverted Toggle value (true becomes false and vice versa)\n" +
                    "Supports EditorAndRuntime mode"));
            }
            
            if (extendedObject.ApplyModifiedProperties())
            {
                // If there were changes, mark object as dirty
                EditorUtility.SetDirty(toggleExtended);
            }
        }
    }
    
    /// <summary>
    /// Ensures ToggleExtended component exists on the Toggle
    /// </summary>
    /// <param name="toggle">Toggle to check</param>
    /// <returns>ToggleExtended component</returns>
    private ToggleExtended EnsureToggleExtended(Toggle toggle)
    {
        ToggleExtended toggleExtended = toggle.GetComponent<ToggleExtended>();
        
        if (toggleExtended == null)
        {
            // Add component silently for all selected objects
            foreach (var obj in targets)
            {
                Toggle t = obj as Toggle;
                if (t != null && t.GetComponent<ToggleExtended>() == null)
                {
                    toggleExtended = t.gameObject.AddComponent<ToggleExtended>();
                    // Hide component from inspector
                    toggleExtended.hideFlags = HideFlags.HideInInspector;
                    toggleExtended.Initialize();
                    EditorUtility.SetDirty(t);
                }
            }
            
            // Get the component for the main target
            toggleExtended = toggle.GetComponent<ToggleExtended>();
        }
        
        return toggleExtended;
    }
} 
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;

#if UNITY_EDITOR
using UnityEditor;
#endif

[System.Serializable]
public class ToggleBoolEvent : UnityEvent<bool> { }

[RequireComponent(typeof(Toggle))]
[ExecuteAlways] // Allows execution in editor
public class ToggleExtended : MonoBehaviour
{
    [SerializeField]
    private ToggleBoolEvent m_OnValueChangedInverted = new ToggleBoolEvent();
    
    private Toggle m_Toggle;
    private bool m_IsInitialized = false;
    
#if UNITY_EDITOR
    private bool m_PreviousToggleValue;
    private bool m_EditorUpdateRegistered = false;
#endif

    /// <summary>
    /// Event that is called with inverted Toggle value
    /// </summary>
    public ToggleBoolEvent OnValueChangedInverted
    {
        get { return m_OnValueChangedInverted; }
        set { m_OnValueChangedInverted = value; }
    }

    void Awake()
    {
        // Hide this component from inspector
        hideFlags = HideFlags.HideInInspector;
        Initialize();
    }

    void Start()
    {
        Initialize();
    }

    void OnEnable()
    {
        // Hide this component from inspector  
        hideFlags = HideFlags.HideInInspector;
        Initialize();
        
#if UNITY_EDITOR
        // In editor, subscribe to EditorApplication.update
        if (!Application.isPlaying && !m_EditorUpdateRegistered)
        {
            EditorApplication.update += EditorUpdate;
            m_EditorUpdateRegistered = true;
            
            // Initialize starting value
            if (m_Toggle != null)
            {
                m_PreviousToggleValue = m_Toggle.isOn;
            }
        }
#endif
    }

    void OnDisable()
    {
#if UNITY_EDITOR
        // Unsubscribe from EditorApplication.update
        if (m_EditorUpdateRegistered)
        {
            EditorApplication.update -= EditorUpdate;
            m_EditorUpdateRegistered = false;
        }
#endif
    }

#if UNITY_EDITOR
    /// <summary>
    /// Editor update for tracking Toggle changes
    /// </summary>
    private void EditorUpdate()
    {
        if (m_Toggle == null || Application.isPlaying) return;

        // Check if value changed in editor
        if (m_Toggle.isOn != m_PreviousToggleValue)
        {
            OnToggleValueChanged(m_Toggle.isOn);
            m_PreviousToggleValue = m_Toggle.isOn;
        }
    }
#endif

    /// <summary>
    /// Initializes component with Toggle
    /// </summary>
    public void Initialize()
    {
        if (m_IsInitialized) return;

        m_Toggle = GetComponent<Toggle>();
        
        if (m_Toggle == null)
        {
            Debug.LogError("ToggleExtended: Toggle component not found!", this);
            return;
        }

        // In runtime subscribe to event
        if (Application.isPlaying)
        {
            m_Toggle.onValueChanged.AddListener(OnToggleValueChanged);
        }
        
#if UNITY_EDITOR
        // In editor save current value
        if (!Application.isPlaying)
        {
            m_PreviousToggleValue = m_Toggle.isOn;
        }
#endif
        
        m_IsInitialized = true;
    }

    /// <summary>
    /// Handler for Toggle value change
    /// </summary>
    /// <param name="value">New Toggle value</param>
    private void OnToggleValueChanged(bool value)
    {
        // Invoke inverted event
        try
        {
            m_OnValueChangedInverted.Invoke(!value);
        }
        catch (System.Exception e)
        {
            Debug.LogError($"Error invoking inverted event: {e.Message}", this);
        }
    }

    void OnDestroy()
    {
        // Unsubscribe from event when component is destroyed
        if (m_Toggle != null && Application.isPlaying)
        {
            m_Toggle.onValueChanged.RemoveListener(OnToggleValueChanged);
        }
        
#if UNITY_EDITOR
        if (m_EditorUpdateRegistered)
        {
            EditorApplication.update -= EditorUpdate;
            m_EditorUpdateRegistered = false;
        }
#endif
    }

    /// <summary>
    /// Adds listener to inverted event
    /// </summary>
    /// <param name="call">Method to call</param>
    public void AddInvertedListener(UnityAction<bool> call)
    {
        m_OnValueChangedInverted.AddListener(call);
    }

    /// <summary>
    /// Removes listener from inverted event
    /// </summary>
    /// <param name="call">Method to remove</param>
    public void RemoveInvertedListener(UnityAction<bool> call)
    {
        m_OnValueChangedInverted.RemoveListener(call);
    }

    /// <summary>
    /// Removes all listeners from inverted event
    /// </summary>
    public void RemoveAllInvertedListeners()
    {
        m_OnValueChangedInverted.RemoveAllListeners();
    }
}