is it possible to write the function below in just one foreach loop?
i have already try it with List<object>
but i do not bring this to work.
private void SoundsOnButtons()
{
Button[] buttons = Resources.FindObjectsOfTypeAll<Button>();
Toggle[] toggles = Resources.FindObjectsOfTypeAll<Toggle>();
foreach (Button obj in buttons)
{
obj.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry eventEnter = new EventTrigger.Entry();
eventEnter.eventID = EventTriggerType.PointerEnter;
eventEnter.callback.AddListener((eventData) => {
Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
});
obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
}
foreach (Toggle obj in toggles)
{
obj.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry eventEnter = new EventTrigger.Entry();
eventEnter.eventID = EventTriggerType.PointerEnter;
eventEnter.callback.AddListener((eventData) => {
Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
});
obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
}
}
thx for help. pezezzle
---------------EDIT---------------
Thanky you @derHugo this was exactly what i looking for but why is this not possible?:
private void AddToList<T>() where T : Selectable
{
List<T> list = new List<T>();
Button[] buttons = Resources.FindObjectsOfTypeAll<Button>();
Toggle[] toggles = Resources.FindObjectsOfTypeAll<Toggle>();
list.AddRange(buttons); // Here i get a error cannot convert !
list.AddRange(toggles); // Here i get a error cannot convert !
foreach (var obj in list)
{
obj.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry eventEnter = new EventTrigger.Entry();
eventEnter.eventID = EventTriggerType.PointerEnter;
eventEnter.callback.AddListener((eventData) => {
Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
});
obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
}
}
CodePudding user response:
You could, yes.
I know this is not exactly what your title is asking for but in my opinion your code would be already way better just extracting the inner block into a method and do
private void SoundsOnButtons()
{
var buttons = Resources.FindObjectsOfTypeAll<Button>();
foreach (var obj in buttons)
{
AddTriggerEvent(obj.gameObject);
}
var toggles = Resources.FindObjectsOfTypeAll<Toggle>();
foreach (var obj in toggles)
{
AddTriggerEvent(obj.gameObject);
}
}
private void AddTriggerEvent(GameObject target)
{
var eventTrigger = target.AddComponent<EventTrigger>();
var eventEnter = new EventTrigger.Entry();
eventEnter.eventID = EventTriggerType.PointerEnter;
eventEnter.callback.AddListener((eventData) => {
Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
});
eventTrigger.triggers.Add(eventEnter);
}
This way you stay fully flexible and maintainable while it is as performant as doing all in a single loop - maybe even better since you don't waste any implementation or execution performance on uniting the two collections into one first.
You could even go one step further if you really want to and make your own generic wrapper method again like e.g.
// "Selectable" is the most specific common base class of UI.Button and UI.Toggle
// You could also use "Component" in order to just allow any component as type parameter
private AddEventTriggersToAll<T>() where T : Selectable
{
var instances = Resources.FindObjectsOfTypeAll<T>();
foreach (var obj in instances)
{
AddTriggerEvent(obj.gameObject);
}
}
and then just do
private void SoundsOnButtons()
{
AddEventTriggersToAll<Button>();
AddEventTriggersToAll<Toggle>();
}
If you really want to go for a single loop you could use e.g. Linq Concat
foreach(var obj in Enumerable.Empty<Selectable>().Concat(buttons).Concat(toggles)
{
AddTriggerEvent(obj.gameObject);
}
which basically equals more or less doing
var list = new List<Selectable>(buttons.Length toggles.Length);
list.AddRange(buttons);
list.AddRange(toggles);
foreach(var obj in list)
{
AddTriggerEvent(obj.gameObject);
}
As a very general note though: Are you sure you want to use Resources.FindObjectsOfTypeAll
and not maybe rather simply Object.FindObjectsOfType
?
Update - Answer to your edit
While you can add instances of Button
or Toggle
to a List<Selectable>
you can not do so on a generic List<T> where T : Selectable
because here you only limit the type downwards.
In other words since Button
is a Selectable
it would theoretically be totally valid to call AddToList<Button>
BUT what you then would try to do is basically
List<Button> list = new List<Button>();
Toggle[] toggles = Resources.FindObjectsOfTypeAll<Toggle>();
list.AddRange(toggles);
=> can't work of course!
However, I don't see a need for your method to be generic at all ... why not simply have
private void AddToList()
{
var list = new List<Selectable>();
var buttons = Resources.FindObjectsOfTypeAll<Button>();
var toggles = Resources.FindObjectsOfTypeAll<Toggle>();
list.AddRange(buttons);
list.AddRange(toggles);
foreach (var obj in list)
{
obj.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry eventEnter = new EventTrigger.Entry();
eventEnter.eventID = EventTriggerType.PointerEnter;
eventEnter.callback.AddListener((eventData) => {
Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
});
obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
}
}
BUT note that AddRange
and working with List<T>
in general is pretty expensive if you don't assign the expected capacity beforehand since while the list grows and internally exceeds the current maximum capacity it all the time has to reallocate and move around the underlying array!
You really gain nothing by "merging" both arrays together just to have a single loop. IF you want to go that way despite everything I said you should rather do
var buttons = Resources.FindObjectsOfTypeAll<Button>();
var toggles = Resources.FindObjectsOfTypeAll<Toggle>();
var list = new List<Selectable>(buttons.Length toggles.Length);
list.AddRange(buttons);
list.AddRange(toggles);
as this way you at least allocate the list only once with the correct capacity right away