A bit back I had a question about how to implement a function I wanted to call on several GameObjects to reset them at various points in the game. I wanted to iterate through the objects and call a method on them but unfortunately, with Unity’s strange method of GameObjects not behaving much like objects, you cannot add a method to them, and thus cannot call GameObject.Reset on the list of objects. Since you need to know the component name attached to the GameObject to call such a function there seemed no easy way, save keeping a list of components, to make the calls.
Fortunately with some help, I was taught Interfaces and implemented one in order to do this.
However, recently I have though similar functionality could be implemented by creating a delegate and raising an event to “call the functions”.
So, this is more a conceptual question. When do you use Interfaces and when do you use Events?
Well, GameObjects are treated like objects just fine, they’re just representative of a programming concept of “composition over inheritance”. The extensible side of GameObjects ARE the Components- Components are what you create and attach in order to extend the functionality of the GameObject.
The approach you ended up taking is a good one. Interfaces are a solid way to make a subset of Components that can be accessed and controlled in a special way. The following would work to reset an entire group of Components on a GameObject, and doesn’t require you to keep a list of Components referenced at all times.
foreach(var resetComponent in GetComponents<IReset>())
resetComponent.Reset();
… should work just fine (aside from needing to use a word other than Reset).
And taking this a bit further, if you wanted to, you can make a GameObject extension method to encapsulate this functionality with the following:
public static class GameObjectExtensions
{
public static void ResetComponents(this GameObject GO)
{
foreach(var resetComponent in GO.GetComponents<IReset>())
resetComponent.Reset();
}
}
I would only do this if the reset-able nature of this component sub-group is pretty significant to your framework though- polluting GameObject with a bunch of only rarely-used extension methods starts to get messy.
Back to your original question here though, interfaces over events, well, “it depends” is really all I can say. They’re not mutually exclusive anyways- many solutions that use events in some way across many concrete types are going to also use interfaces to register the delegates.
My advice would be to create a manager component whose only job it is to register and keep track of / fire those reset events across all Components on the GameObject. Then it would only be the one type (the manager) you need to access and deal with from the outside. Once you’ve established a single point of access to reset the components, you can use GetComponents once and then register each of the Reset functions as a delegate to an event handler, or you can call GetComponents each time you need to reset. The difference is in cache invalidation. If you register each component to an event, you need to make sure either:
A) no new reset-able component types are going to be added later, because then you’d have an incomplete list, or
B) if new components are added, they’re added through the manager object and not just AddComponent on the GameObject, so that it can register the new delegate properly
If you just use GetComponents() each time you need it instead, you don’t have to worry about that- new Components can be added/removed at any time and it doesn’t invalidate the cache, because there’s no cache. It’s a small performance hit, but it can be worth it to keep the complexity down.
Either way, it’s the interface you use to find the functions to add delegates for, so this isn’t really “events over interfaces”- it’s both. The real question is just “do I need events for this?”, which I can’t answer. It’s up to you.
This is a good architectural question, but one that is hard to give a universal answer to. In your situation it comes down to whether or not you want a list of objects (that implement an interface) or a list of functions (any form of multicast delegate, or an actual list of functions). You would most likely want to use an interface. Here are some things to consider:
If your reset system requires an object to have anything more complex than a single function call, use an interface. This is so you can easily manage and modify its requirements.
If you have many different classes that need resetting, use an interface. This has the benefit of making it harder to forget to subscribe, or subscribe incorrectly.
If your class is fundamentally incorrect if it is not at some point reset, consider an interface.
If your resettable objects should not have to subscribe themselves (this is potentially a good idea), use an interface and have something else add them to the list of resettables.
If your reset system needs to be able to easily call any arbitrary function, not just something implementing IResettable, use an event.
If you ever need the list of resettable objects for something else, use an interface.
If you want the reset system to unsubscribe resettables by itself, use an interface. As opposed to functions, this makes unsubscribing a specific object trivial.
If your reset system makes more sense as part of a larger system of events, keep it as an event.
Keep in mind it’s also possible to do things like adding a method that’s exposed by an interface to an event. Using both at the same time is also an option.
I’ll take exception with Unity treating GameObject like objects just fine. As my OOP prof was fond of saying “Object are objects are objects”. This isn’t the case with Unity’s GameObjects which implement only one design pattern.
On the Interface side, Lysander noted, “The following would work to reset an entire group of Components on a GameObject, and doesn’t require you to keep a list of Components referenced at all times.” Is there a similar method if there is only in general one component on each Object in the group so that a list of GameObjects does not need to be kept?
Right now, a list of those GameObjects is kept in a game manager and updated in the editor. It would be nice, if not too time consuming, to populate this array automatically at level start, as new objects for it are not creatednor destroyed while playing the level.
I might be misunderstanding, but GameObject instances are normal C# objects. They are objects that are handles to physical things within the scene, as well as the components attached to them. If a GameObject needs some functionality, you add a MonoBehaviour derived component to it. GameObject ostensibly has a list of attached MonoBehaviours that you can access with GetComponent. Conversely, you can access the GameObject that the MonoBehaviour is attached to with this.gameObject.
If you want a MonoBehaviour to automatically register and unregister itself with a manager or something, the simplest way is to do it in Unity’s message methods such as Awake(), Start(), OnDestroy(), OnDisable(), OnEnable(), etc in each class where you want this to happen. In this case you don’t need to worry about GameObject. All you are doing is passing the MonoBehaviour as your intended interface.
Not always. You aren’t allowed to extend classed with the ‘sealed’ modifier, like GameObject. Preventing inheritance of concrete classes is a perfectly ordinary design decision.
Of course there are exceptions. I was speaking of “ordinary C# classes”. The lack of inheritance on GameObjects does make many design choices more difficult.
Even from a composition standpoint, I cannot extend the class itself. I can only attach a new class too it thus requiring knowledge of the class to call it and breaking a key benefit of OOP IMO.
Personally, I think it does not make it more difficult, once you get used to writing components. Once the projects grows, inheritance can quickly get you stuck at some point, and sooner or later you can identify bad design decisions and you might regret the overall design.
So, GameObjects are more like a container class, with as little information as required in order to manage a complex scenery in which many of them may reside. That’s really their only purpose in the design UT came up with.
There’s really nothing you could override or change about GameObjects themselves, because they do not do a lot by themselves, but instead, their behaviour is defined and driven by their components.
So the only thing you’re able to do is to add, but not change behaviour. And this works just as fine with components.
However, if you still like to have some sort of object type that you generally speak to, you can still implement your own Component that you address, and which follows your idea of designing the overall architecture.
Then, the implementing component provides the interface, some sort of facade, that you talk to, whereas its internal behaviour is a composition of components. To the caller, that’s almost the same as if GameObject could be extended directly, except that the component needs to be fetched at least once, unless linked via inspector or injected via some other DI.
Undoubtedly, and this is the kludge I am using to work around Unity not having object that may be extended through inheritance. Certainly a far more convoluted method than inheriting and adding a method to the GameObject.
If you have 2 objects… A and B and you want to send a message from A to B called ‘Reset’.
Your 2 options your describing are like this:
A finds all components on B which implement IResetable then call Reset on each of them.
B finds A and registers for an OnReset event
The reversal going on is that in 1), A knows about B, but B doesn’t necessarily need to know about A. And in 2) B knows about A, but A doesn’t necessarily need to know about B.
So that’s when you’d use them… if you’re design requires A knowing B, you’d send a message to B (in your case you’re using an interface, though that’s not the only way to do it). Where as if your design predicates that B knows about A, then A would dispatch an event.
Now what I mean by this is can A or B be easily known?
So a real world example of 1) where A knows of B would be… how about an AOE, like a spike pit. The entity doesn’t know about the spike pit (or rather it’d be weird to write your code so that your entity listens for ALL spike pits)… but rather instead the spike pit knows about the entity (it’s OnTriggerEnter message is received from the physics engine). And the spike pit would send a message to the entity that it’s striking it with damage (may that be directly to a ‘HealthMeter’ script… or indirectly via a ‘IDamageReceiver’ message receiver).
And a real world example of 2) where B would know of A… how about a UI Button. A Button doesn’t know how it’s going to be used or what it actually does. All it knows is that it’s visual, it can be clicked, and that it dispatches an event declaring that it’s been clicked. You wouldn’t have the button find everyone that needs to know it was clicked… because how would it know that? Instead what objects are concerned with the clicking of the button will register for the click event. (may that be a UnityEvent, or a C# event/delegate, or any other number of event systems you could find out there).
I’m not sure the context of your Professors quote… but I’m not sure what that has to do with GameObject’s design. And I have a feeling you’re mis-applying their quote here.
In the case of most contemporary OO languages like C#, Java, even python… when we say “an object is an object is an object”, what we mean is that all structures in the language are objects in some manner or another. Like in C# all data types inherit from ‘System.Object’, that is the root of the inheritance tree. So thusly ALL objects in your code can be treated as a System.Object. Int32, string, GameObject, enums, all of it can be cast to ‘object’ and treated as that type.
No it can’t.
A normal C# class can be inherited from and extended… barring it hasn’t been sealed.
You can’t inherit from an object (where an object is an instance of some class). And dont conflate this with how System.Type allows you to have an object that is the object identity for any given class enabling reflection. And how you can technically inject a new class inheritance via reflection and emit… you’re not really inheriting from the object, you’re using objects to write new IL that is then injected into the runtime to then be compiled via the JIT. And anyways just because these objects technically in a loose manner support a way of emitting new inherited classes into the domain does not mean objects TYPICALLY can be inherited from.
If you extend any class and ‘add’ any members to it. You would need knowledge of the class to call it.
Lets say you wanted to add the method Reset to GameObject… what would you do if you used inheritance?
public class ResettableGameObject : GameObject
{
public void Reset()
{
//do reset logic
}
}
OK…
So now what? How do you call Reset?
GameObject go = *some gameobject*;
go.Reset(); //womp womp - Reset is not a defined member of GameObject
You’d have to KNOW it’s a ResettableGameObject. And thusly it’d already be typed as that:
ResettableGameObject go = *some resettablegameobject*;
go.Reset();
Or you’d have to cast it:
GameObject go = *some gameobject*;
((ResettableGameObject)go).Reset();
And how is this any different from saying:
GameObject go = *some gameobject*;
go.GetComponent<IResettable>().Reset();
Yes the statement is different… but work had to be done, knowledge had to be had.
In both the casting and the retrieving of component all you’ve done is said “give me this object translated in a manner that it can be treated as having a ‘Reset’ method on it”.
…
Now this is as opposed to if you wanted to override functionality of the base class. That you wouldn’t need to know the sub type overriding the base type.
this is all to do with the composition portion of GameObject. Again, if you were trying to override these, you’re doing it wrong.
SetActive
this is in relation to activeInHierarchy and activeSelf… and do I have to repeat myself? You’re doing it wrong.
BroadcastMessage
SendMessage
SendMessageUpwards
OK… this is the only group of methods I could maybe see wanting to override. Maybe you wanted to write a message translator or something that blocked certain messages, or converted the name of the method or… several nitty gritty things I could maybe see in a weird edge case wanting to override from whatever bizarre reason.
BUT
I’d argue you’re still doing it wrong if only because the SendMessage system is an old school thing that Unity created back way in the beginning of Unity. It’s still technically used in certain capacities, especially on the internal side (with the Update, OnTriggerEnter, etc all sort of technically relying on the reflective inner working of SendMessage).
But it’s highly suggested to not even use them.
And instead favor the very ‘interface’ messaging system brought up in the OP of this thread.
They’re effectively the same thing… one using magic strings, the other using statically typed interfaces.
I can repeat the quote again if necessary, but no there is no misapplication here.
Except as you mention in the next breath, when it can’t.
Other than my mistaken use of object where I meant class, i respectfully disagree. Yes , it can,
Again, I disagree. In the case of inheritance, I need ONLY know the ResetableGameObject. I need not know anything of it’s structure. (The class is defined as being resetable). In the case of composition, I do need to know something about the structure. Either the component name that implements the Reset method or that this PARTICULAR gameobject (as opposed to others that do not) implements an IResetable Interface,.
(snipped as overriding functions is not what was requested)
Please explain how it was properly applied if it was.
What next breath?
My next breath is “No it can’t.”, which has to do with you can’t inherit from any old object. How does this contradict the paragraph of mine you’re quoting?
No you can’t inherit from any normal C# object. If you change the word to ‘class’, yes you can. Because those are different things. We don’t disagree there… you used the wrong word.
If you say “2 + 2 = 5” and I say “no, it equals 4” you can’t say “I meant 4, so I respectfully disagree”. No… you don’t disagree, you agree.
If you need only know the ResetableGameObject than you do need to know something about the structure. You need to know that it IS a ResetableGameObject. And in the case of composition you need only know that it HAS a IResettable.
The structure has just moved. Instead of an inherited structure, it’s a composited structure.
But you need to KNOW either way.
You can’t just treat a GameObject like a ResetableGameObject. And you can’t just treat a GameObject as if it has a IResettable. You have to KNOW if it is a ResetableGameObject or has an IResetable.
Unless you meant something else… if so, explain what you mean. Give me an example.
You have made the assertion my professor’s quote was misapplied. The burden of proof falls on you.
I quoted it…
Or you agree with me!
Such is the nature of Objects though. The class it is instantiated from determines its functionality. So no, I need not know anything about its structure other than what class it is. Contrast that with composition in which I may know the class an object was instantiated from (GameObject) and still not know what functionality it possess.
I agree the structure has moved. It has moved from being an intrinsic part of the object to an attached part of the object. You cannot call go.Reset. If you wish to use the object’s functionality, you must either know the structure of the object (which component implements the method) or you must build a (kludged IMO) bridge to the functionality.
Yes. Knowledge that is intrinsic to the class it was derived from in one. Not in the other.
Please call Door.Reset() via composition. I know it is a ResetableGameObject. Therefore I know it has this functionality.
You know it is a GameObject. Exactly what functionality does it have?
Good lord, you really do have a way of getting under peoples’ skins @lordofduct . I mean, as soon as he said “I’ll take exception if I want”, I was done with the conversation, but you just keep at it far after they stop making any sense. I’m genuinely curious if this is some form of stress relief for you, because I can’t really see any other benefits to the exchange after the first couple of posts- certainly, no one’s going to come through later and say “oh, this is exactly the information I needed!”, and from the looks of it this guy is never going to admit that he’s way out of left field with his secondhand interpretation of “real objects”.
You’re usually pretty much dead-on accurate, and yet reading the conversions you participate in is like watching a marriage fall apart, as all parties involved slowly devolve into howling, neurotic versions of their former selves.
lol, yeah, that’s why I didn’t go into the most recent post and just shook my head and unfollowed the thread. Only reason I noticed this response is you dropped the mention.
And yes, you’re correct, it’s usually a form of stress release for me. Until it’s not.
As I said, previously, thank you for the help. If you are going to continue with off topic posts however, I’ll probably respond in kind.
It was rather like someone asking for help crossing the street at a crosswalk and being told you don’t need to cross the street at the cross walk, there’s a perfectly good foot-bridge 3 blocks down…