- Home /
How to create a generic monobehavior referencer
Hello everyone,
I have a question that, although I consider somewhat basic, can't seem to either find online or figure out. Let's say I have a prefab "A" and it has two children Gameobjects, (a light and an audiosource). After instantiating this prefab, I would like to reference both elements so I can, for example, dim the lights. Now, there are several ways I know you can go about doing that: using transform.find and/or FindWithTag are both usable methods, but that I tend to avoid them since they are slow and not very robust. My default "go to" method would be to create a component attached to A, that has references to both things, the light and the source.
public class PrefabAReferencer : MonoBehaviour
{
public Light m_Light;
public AudioSource m_Audiosource;
}
Now, this works just fine and allows me to access stuff pretty efficiently, but I dislike having to create a new file for references every time I create a new prefab (or, for that matter, having to update the reference script if, for example, I add a new children to prefab A).
I would like to create a script that I could attach to a Gameobject and that had a reference to any other component. My initial idea was to use generics since they are exactly the way I would go about solving this issue. Something like:
public class GenericReferencer<T> : MonoBehaviour
{
public T m_Ref;
}
Now, sadly, that wont be displayed on the inspector since Unity has not way to know the type of T and cant properly serialize the class. I can create a class that inherits from GenericReferencer, using a concrete type, but then I run into the same problem of needing to do this for every new type I need.
The best solution I have managed to come up with is to just have an Unity Object as the reference and then access it whenever needed, doing a cast for the desired type. Although this solution works, I would still like to have a little bit more of robustness by having the Type explicitly stated on the referencer.
Is there any way to do something similar to what I'm trying to? Thanks for the help :)
Answer by sacredgeometry · Dec 07, 2020 at 08:51 AM
Isn't this what GetComponentInChildren<T>
does?
If your children are different for each game object then I think you are going to have to write different code to ideally reference them. At least one for every variation.
i.e. if you have a bunch of different prefabs with the same configuration of children then you could use the same script but as soon as they differ you will have to differentiate the code.
The only other way to do it would be to stick them in a collection but that in itself would require a bunch of faff with constantly casting them to the derived type you want instead of to their base component type.
Granted this could be abstracted away but the performance overhead cant really be and at that point all you have done is create a less efficient version of GetComponentInChildren()
What is it that you are trying to solve here? If it's trying to have global references to certain objects you can definitely achieve that in a bunch of ways from static references to DI'ing references to them.
I am still not sure what you are trying to achieve but maybe if you are sold on this pattern interfaces with extension methods may help?
i.e. you could have an empty ILightable interface which had a GetLights() extension method.
Then all you would need to do is add ILightable to your class and it would give you access to that method on any instances of it.
Something like:
public interface ILightable {}
public static class ReferenceExtensions
{
public static Light GetLight(this MonoBehaviour mb)
{
return mb.gameObject.GetComponentInChildren<Light>();
}
}
public class GenRefTest : MonoBehaviour, ILightable
{
void Update()
{
var lights = this.GetLight();
}
}
public class AnotherGenRefTest : MonoBehaviour, ILightable
{
void Update()
{
var lights = this.GetLight();
}
}
Thx for your answer.
I don't thinks that such and interface would solve the issue, since I'm explicitly trying to avoid using specific types for each children that I want to reference: I would require a new extension method for every new object type that I want to reference. For example, lets suppose I know have a new PrefabB that has UI Image and I want to reference it and change its color. To so so I would need to create a new interface that would get the Image component This would have to occur for every new type that I wish to reference.
GetComponentInChildren would kind of work at getting me an element of the desired type, but it has many negative aspects (as mentioned above). If the prefab had multiple lights, for example it would already be complicated to pick the exact one that I need. If I want a specific light, I could write something like my PrefAReference above and just drag and drop the exact light that I need to the m_Light field on the inspector. I'm trying to simulate such behavior but for any type I wish (images, audiosources, any unity object in fact).
It sounds like what you are after is just defining types. Thats the best mechanism for working with this problem and if you have enough types to make this a problem then the problem isn't the types its how you have structured the other part of the project.
If you have a system of generic composition i.e. components and you want to have a specific reference to that then defining that specific reference is exactly what types do.
You could use generics if you really wanted but it wouldn't stop you from running into the same problems.
Either A. you have a property of T for each component type and then have to deal with collections i.e. the same problem you just mentioned with GetComponents.
B. You tag each object with some sort of value used for differentiation and then grab the components like that (which still has the same problem for multiple instances of the same component.
or you create solid references.
One thing to note is that generic type arguments will need to be overloaded to allow for n type arguments. For example if you want to support methods or classes with anywhere between 1 and 10 type arguments you need to create 10 classes/ methods each dealing with that.
It's not a very graceful solution for variable argument types. (thats not to say it isnt how other people have dealt with it often just putting a hard limit on the amount of supported types.
Your answer
Follow this Question
Related Questions
Custom Inspectors for generics and tweaking default inspectors 1 Answer
Serializable class using generics 3 Answers
Past a Type Script from the inspector 0 Answers
How can I get all the options this guide have in the inspector? 1 Answer
[SOLVED]How to use attributes from array of custom class instances? 1 Answer