- Home /
Persistent listeners for Button.onClick are removed at runtime, if buttons are instances of prefab
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.
I have the same problem works for other buttons in scene but doesn't work for these ones weird...
If possible, answer the question. If not, write a comment.
Answer by abcjjy · Nov 26, 2017 at 09:16 AM
After hours of trying and searching, I find a workaround. It is a workaround because it is said clearly in the doc that the api is not recommended to be used after 5.3.
Call EditorUtility.SetDirty(b) after modification.
Undo.RecordObject seems not work on button event.
Your answer
Follow this Question
Related Questions
Set Prefab position when added to scene (Edit mode) 1 Answer
Breaking a prefab connection 7 Answers
UnityEditor on Tag Update event 0 Answers
How to know if a GameObject is a prefab? 15 Answers
Can I create a child gameobject on a prefab with an editor script 2 Answers