In my menu scene I want to attach a listener to the onClick event of all buttons, so every button plays a sound when clicked.
This is too much work to do manually, so I wrote a ButtonSoundManager
component and attached it to the Canvas. It contains a public void PlaySound()
method that plays the sound.
In the same component I wrote this method to attach the listener to all buttons via an editor menu item:
#if UNITY_EDITOR
[MenuItem(@"Menu Sounds/Attach or Fix menu sounds on this scene")]
public static void AttachMenuSound()
{
// Find the ButtonSoundManager instance, since we are in a static method
ButtonSoundManager manager = GameObject.Find("Canvas").GetComponent<ButtonSoundManager>();
Button[] allButtons = manager.GetComponentsInChildren<Button>(true);
foreach (Button b in allButtons)
{
// Register click listeners
UnityEngine.Events.UnityAction action = manager.PlaySound;
// RemovePersistentListener will remove all matching listeners, even if there
// are multiple of them.
UnityEditor.Events.UnityEventTools.RemovePersistentListener(
b.onClick, action);
UnityEditor.Events.UnityEventTools.AddPersistentListener(
b.onClick, action);
}
}
#endif
In the editor this works exactly as expected. After clicking this new menu item, in the inspector I do see the listener attached to the onClick event of all buttons.
However, as soon as I enter play mode, these listeners are immediately removed. When I exit play mode the listeners are still gone, as if they never existed. If I enter play mode and then click the menu item, I can attach the listeners at runtime and they work.
What is going on? Why are the listeners removed at runtime? I have near zero experience writing editor code so I may be making a stupid mistake.
Also I am on Unity 5.4.1f1 and am not able to update due to company policies.
UPDATE The buttons in my scene are instances of a prefab, which does not have event listeners. If I do “Break Prefab Instance”, the listener is no longer removed at runtime. However I want to keep my instances as instances.
UPDATE 2 Adding a call to Undo.RecordObject
before removing/adding listeners seems to fix the problem for most buttons. However for the remaining buttons, my code either duplicates the last listener (there are other listeners on the buttons), or adds an empty listener, instead of adding manager.PlaySound
.
UPDATE 3 I still have no idea why, but for those buttons where my code doesn’t work, I manually added the listener and ran the code again, and now it works perfectly. The answer to the initial question is Undo.RecordObject
. Since the question is still in moderation, I cannot post an answer, so I am leaving it here.