etc etc. However, because they have no shared base class, we can’t easily write reusable generic classes that support either GameObject or Component as the generic type.
Because of all of these shared functions, it seems clear that the two classes should share an interface:
public interface IGameComponent {
T GetComponent<T>();
T GetComponentInChildren<T>();
T[] GetComponentsInChildren<T>();
//etc etc
void SendMessage(string methodName, object value, SendMessageOptions options);
//etc etc
Transform transform { get; }
}
Adding such an interface would not require any refactoring or affect the functionality of the engine in any way, and would not break anyone’s existing code; it would simply make Unity developers’ lives easier in the future.
Instead of doing this (this is silly example code, but conveys the idea):
public void StopObject(GameObject go) {
Rigidbody body = go.GetComponent<Rigidbody>();
if (body != null) body.velocity = Vector3.zero;
}
public void StopObject(MonoBehaviour mb) {
Rigidbody body = mb.GetComponent<Rigidbody>();
if (body != null) body.velocity = Vector3.zero;
}
we could do this:
//This would work for both GameObjects and MonoBehaviours!
public void StopObject(IGameComponent gc) {
Rigidbody body = gc.GetComponent<Rigidbody>();
if (body != null) body.velocity = Vector3.zero;
}
Is there any reason such an interface has not been added?
I’m not sure why you’d ever want to use MonoBehavior instead of Component or something you derived from monobehavior…
MonoBehavior is largely used as a plug for interfacing with C++ part of the engine - the game engine looks for specific function names and then calls them.
In scenario you described you’d stick with using one specific type throughout the project.
While it does sound inconvenient, most of the time you don’t need unified interface and either work with gameobject to access its transform properties, or work with specific components. So, basically, you use gameobject if you want to access .transform, and Component for everything else.
Do be aware that dogmatic C# programming is not very suitable for unity engine. For example, one common issue is that overuse of immutability will result in garbage generation.
you’d be much happier if you use C# as scripting language and making things “good enough”, instead of trying to make things “perfect”.
Conceptually a GameObject is an entity in your scene, but logically it is a container for Components. In other words, it associates components with each other based on what entity they are a part of in a scene.
GameObjects on their own do not have any functionality in addition to that. They don’t do anything. All functionality is provided via Components.
One fundamentally important function that entities have in a scene is management and representation of relationships with other entities. That is what a Transform does. Note that there are in fact two types of related but different relationship being handled by the Transform: the heirarchical relationship (ie: parent/children) and the locational relationship (position / rotation / scale relative to parent).
Because this stuff is so fundamental to how GameObjects work and Unity decided to enforce a 1:1 relationship anyway it’d make sense to make them the same object. However, there are pros and cons either way. Of particular note:
There are potentially large performance benefits to splitting the transform data out, as it can be operated on more efficiently.
It’s consistent with how every other Component in Unity works.
I know that a base class or Interface is the textbook way to handle this kind of thing. Still, can you not just write your stuff to use Component, and then use gameObject.transform instead of gameObject directly? I wouldn’t be concerned with the idea that “Transform is closer to Monobehaviour” because, practically, I think it still achieves what you want?
Perhaps there are usecases I’m overlooking? Personally, I use Transform instead of GameObject a lot in general. The reason for this is that often when I reference a GameObject I want to know its position. So instead of getting a reference to the GO and then having to get its Transform to get the position, since it’s a 1:1 relationship I may as well pass around the Transform directly.
There is some distinction, the base Component’s can’t be disabled. Even then I would still choose Behaviour rather than MonoBehaviour, but it is a distinction worth noting.
I think it makes the most sense to pass around the object that matches the method, if its got something to do with position, rotation, scale use Transform, if its got something to do with collections of components use GameObject.
A method that needs hierarchy is a tricky one, as you say its stored on the transform, so maybe it is most logical to use Transform, but for some reason it sits better with my mental model to ues GameObject unless the method is specifically about using the hierarchal relationships to affect the transform (position/rotation/scale).
Of course its all fine grained detail and a lot to do with personal preference, but interesting to understand what others do.
Again, this is mainly an issue with writing generic classes. I don’t want to paste entire classes here to demonstrate the idea; anyway, I think it’s silly to argue against adding a shared interface simply because you think there are workarounds. Adding a shared interface wouldn’t hurt anything, but opens up more options for code flexibility.
public class GenericClass<T> where T : MonoBehaviour allows T to be any type that extends MonoBehaviour. MonoBehaviour is the default base class for all Unity scripts; in other words, this would allow any user script as T. We could also do where T : Component or Behaviour or whatever, but I usually just use MonoBehaviour because that’s the default base for user scripts.
Good catch. But this is now a part of Behavior class.
Either way, what OP sees is likely a mix of historical baggage and convenience.
Some of those methods are certainly forwarders.
Meaning Component.transform is likely a shorthand for Component.GameObject.transform, and same goes for Add/Remove component methods on Components.