SOLID Principles in Unity

SOLID Principles in Unity

In this video we’re going to learn about the SOLID principles a refactoring this MonoBehaviours so it’s more flexible and easier to use. My name is Charles and this is Infallible Code; a channel designed to help you become a better game developer. If you want to learn more about programming, Unity, and Game Development then be sure to subscribe to this channel and hit that notification icon so you’ll be notified when every tutorial is made available. Now before we dive into the actual code refactoring why don’t we take a quick moment to look at the example scene because it’s important to understand why we actually need to refactor it before we touch the code. So first things first why don’t we go ahead and run the scene. So the scene itself is very simple. It consists of a basic environment that was created using 3D models that I found for free on the Unity asset store, as well as the FPS player controller that comes bundled with the Unity standard assets package. But what I want us to focus on is the functionality that’s provided by the script that we’ll be refactoring in this tutorial, which is the SelectionManager script. Whenever I mouse over one of these dark gray objects we can see that the SelectionManager script replaces its default material with a special highlight material that’s configurable in the editor. That’s great and all but I’d really like to replace this behavior with something different. For instance I found an asset on the Unity asset store that outlines GameObjects in the scene. I think that would look much cooler. But unfortunately the way the SelectionManager code was written, it’s not very extensible. And it would be very hard to work that code in. So what we’re gonna have to do is use the SOLID principles to refactor the SelectionManager SelectionManager so it’s implementation can support any behavior that we want moving forward. Why don’t we switch over to the code editor and do that now. This is the SelectionManager script. It’s responsible for all of the behavior that we just saw in the scene, and it does it all in its Update method. Now when thinking about the SOLID principles the first one that comes to mind is the Single Responsibility Principle. But what exactly is the selection manager responsible for? Why don’t we break it down using some comments to figure that out. The first thing that the selection manager is responsible for is creating a ray and casting it into the scene. The ray is used in the second responsibility which is determining what was actually selected. In this case the selection manager uses a tag comparison to determine what the ray actually hit and whether or not that hit is actually selectable. The last thing the selection manager is responsible for right now is responding to deselection and selection. And that actually occurs in two different places, but it really is the same responsibility. We can copy that down here as well. So we can see that the selection manager is responsible for three different things but for this tutorial we’re only interested in refactoring one of them, and that’s the response to selection and deselection. So why don’t we wrap this section in the middle with a region just so we can get it out of the way. Alright so we can just minimize that and now we’ve isolated the script to the two pieces that we actually care about. These two bits of code that handle selection and deselection. So eventually we want to apply the Single Responsibility Principle to this script by taking each of these responsibilities and moving them out into their own interfaces so that the selection manager is responsible for one thing; delegating all of the tasks that are required for selection management. But, again, for right now we’re just gonna focus on the selection and deselection response. And, looking at this code and thinking about the refactoring, the next SOLID principle comes to mind is the Open-Closed Principle. So what we’re going to do is replace this logic right here and right here with logic that, instead of updating the material of the selection with a highlight material, instead it’s gonna use a Unity asset that I found on the asset store to just outline the material. The logic looks completely different so in order to do that we’re gonna actually have to delete this code and replace it with the new logic. But I don’t want to do that because that breaks the Open-closed Principle which says that classes should be open for extension and closed for modification. I don’t want to actually have to modify this class code. Instead I’d like to be able to implement a new class that would allow me to modify the behavior without losing the old behavior. So what we’re gonna do is just take this logic here and just pull it up into its own class. And I’m using Rider as my code editor and with Rider, and I believe Resharper as well, we can do that in a really cool way. So the first thing I’m gonna do is I’m going to take these two bits of logic and move them into their own methods. And we can do that using the extract method refactoring. But first I’m gonna take this reference to selection, which is a private member variable, and I’m gonna move it out into its own local variable. Just so it’s out of the way and this code is as isolated as possible. Now I can select this logic and I can extract it into a method. We’re gonna call this OnDeselect and, again, I want to make sure that this transform is being passed in as a parameter. Now we can select next and we can see that all that logic has been pushed into its own method. And you know what? I’m gonna do the same thing for OnSelect. Move this into its own variable, grab it and do the refactoring to extract the method. We’re gonna call this OnSelect, and of course make sure that this transform is being passed in as a parameter. Alright now these are safely out of the way and we could begin the process of moving them into their own class. Before we move them into a class we need a class to move them to. So what I’m gonna do is I’m gonna add a class that we haven’t created yet. We’re gonna call this HighlightSelectionResponse. And I’m gonna just name the variable selectionResponse. And then I’m going to use Rider to create the type for me. Alright we can see it’s been created down here. I’m gonna zoom in and we’re gonna make this inherit from MonoBehaviour. Perfect! Now before we make the move we need to make sure that this HighlightSelectionResponse has all of the member variables it needs. Namely it needs a reference to defaultMaterial and highlightMaterial because now that logic is going to belong to the HighlightSelectionResponse. So what we’re gonna do is scroll up to the top and we’re going to just copy these two variables and move them into the HighlightSelectionResponse class. And since we’re now gonna need to be able to reference them in the SelectionManager, I’m going to temporarily make them public. Now instead of referencing the SelectionManager’s materials, I can go ahead and reference the selectionResponse’s default and highlight materials. Alright perfect. So all that that we just did serves the purpose of allowing us to move these two functions easily into the HighlightSelectionResponse class. So what I’m gonna do is select the OnSelect method and I’m gonna do the refactoring to move this instance method into the HighlightSelectionResponse class. And I’m gonna do the same thing for onSelect. Perfect! So now all this logic has been completely moved out of the SelectionManager class, which means we can go ahead and delete these two material fields. And now if we want we can go ahead and just inline these selection references. Beautiful! That looks much cleaner. However, believe it or not, we haven’t yet applied the Open-Closed Principle yet because if I wanted to change the behavior of the SelectionManager I’d still actually have to modify this code because it depends too tightly on this HighlightSelectionResponse. So what we’re gonna do is apply another SOLID principle the Liskov Substitution Principle. And what we’re gonna do here is take this HighlightSelectionResponse class and we’re gonna derive an interface from it so that instead of depending on this particular implementation we’re gonna rely on an interface. And the interface is going to be completely replaceable by any subtype that wants to alter the behavior of this program. So in Rider I can come down to HighlightSelectionResponse and I can go ahead and extract an interface. I want to make sure that I include the OnSelect and OnDeselect methods, and we just want to name this ISelectionResponse. We want to give it a more generic name because the selection response is going to eventually do more than just highlight but in this tutorial it’s gonna outline and maybe in the future it’s going to do other things like pull the object into an inventory or something like that. So let’s click Next and we can see that now we’ve extracted an interface. And now we have a contract for other subtypes to adhere to, which would allow us to implement whatever behavior we want and ensure that the SelectionManager logic never changes. Because all the SelectionManager should be responsible for is delegating the tasks and in this case it’s delegating the tasks of what to do when a selection occurs and what to do on deselection as well. So in order to complete this refactoring we need to go ahead and replace this HighlightSelectionResponse with the ISelectionResponse. I’m gonna use the use base type where possible refactoring here, and it’s gonna replace that with our ISelectionResponse. And of course we need to implement the Unity Awake method here so that we can go ahead and set that value using the GetComponent method. We will expect that anything that implements ISelectionResponse is a MonoBehaviour so we can grab it from the SelectionManager GameObject and use it in our code. Alright so now that all this is in place why don’t we go ahead and extract these into their own class files. And now the SelectionManager is looking much cleaner, and we’ve taken one step in our refactoring to making this class much more SOLID. So why don’t we hop back into Unity set up our scene and test this code out. So the first thing we’re gonna do is open up the SelectionManager and update it to use the new HighlightSelectionResponse MonoBehavior. And just like before we’re gonna need to set the highlight materials. So I’m going to use the [email protected] and [email protected] for these materials. And now believe it or not that’s it. We’re ready to press play and we can look around our scene and ensure that all of our objects are still being selected and highlighted and the behavior has not been broken in any way by refactoring. So now we can actually switch back to the code editor and implement some new behavior very easily using the SOLID principles. The SelectionManager now adheres to the Open-Closed Principle which means that now we should be able to implement ISelectionResponse in any way we want. And as long as we set the SelectionManager’s selection response to that new implementation the behavior should be modified. Why don’t we go ahead and do that now. I’m gonna create a new class called OutlinesSelectionResponse and I’m gonna make it derive from both MonoBehaviour and I want it to implement ISelectionResponse. Now we can very easily drop in some logic into these two methods. So what I’m gonna do is I’m gonna try to grab an Outline object from the selection, and that’s from that free asset that I mentioned earlier, and once we have the Outline OnSelect what I’m gonna do is set the outline width to 10. And on deselect I’m gonna copy the same logic in here. I’m just gonna set it to 0. And for each of these we should probably check to make sure that the outline is actually there. So I’m gonna check that it just does not equal null for both. And we’re done! We’ve implemented brand new behavior without having to modify the SelectionManager. And this actually brings me to another one of the SOLID principles, the Interface Segregation Principle. So that says that many client specific interfaces are better than one general purpose interface that’s selection manager if we had extracted that into its own interface would have been one general-purpose interface to manage everything but now we split it up and this I selection response interface is a part of this very client specific broken-up piece of software that’s decoupled so that it can manage its own thing and selection manager can manage its own thing and then moving forward as we start to break out those responsibilities each one of those responsibilities will get their own interface now before we switch back to unity and tests out our new behavior we should probably talk about the last solid principle the D which is dependency inversion principle and that states that classes should depend upon abstractions and not concretions now before when we were referencing the highlight selection response class that was depending on a concrete implementation but now all the selection manager does is depend on an abstraction of election response it doesn’t know how it’s gonna response selection all it knows is that on deselect it’s going to delegate that on deselect action somewhere else in the code and same for on select and as we just saw we implement their brand-new response and what we’re gonna see is that instead of highlighting the objects we’re now going to outline them using another asset so let’s switch back to unity and see that in action back in unity to set up this code we’re gonna need to do two things first we’re gonna remove the highlights election response script since we’re no longer gonna be using that implementation we’re going to use the outline selection response instead and next what we’re gonna do is select the four objects in our scene and make sure that we’ve added the outline script to them that came with that asset and I’m gonna set the color to green because that’s my favorite color so that now whenever we mouse over one of these objects the selection manager is going to use the outline selection response to set the width between five and zero depending on whether or not the object has been selected or deselected let’s see what that looks like all right mouse over the sphere ID sure enough it’s been outlined so thanks to our refactoring and the solid principles we were able to introduce brand new behavior with just a couple lines of code in the next video we’re gonna rewrite our selection determination logic so it’s a little bit more sophisticated you won’t want to miss it so thanks for watching and I’ll see you in that video.

43 thoughts to “SOLID Principles in Unity”

  1. For those struggling to use the selection manager outside of this or any project with it already working, here's a hard-earned tip: make sure your camera has a Physics Raycaster component on it.

  2. 📰 Sign up for my newsletter for more free Unity tutorials, tips, & tricks →

  3. I love your tutorials. I like that you take on slightly advanced programming topics, show them in a real-world scenarios/use cases, and explain why the topic makes sense – eg. this video on the SOLID ideas that a lot of teams use.

  4. Great video. Very solid info. I just wonder, what if you want to make the response conditional, f.ex say, if you looking north make them yellow, if not then use the green outline. Something like that. You would have to make a new class with both interfaces? or am i missing something obvious here? 🙂 I will have to watch this a few times

  5. Unity needs to sponsor you instead of some of these other people who upload tutorials. His videos are borderline clickbait. Also, please consider making this little "selection" game into an ongoing project that you use to show how to do other things. One of the things I think that is a problem in the Unity tutorial world is that each video or tutorial sort of shows you how to do something in a vacuum rather than part of a whole "game". I'm not critiquing the way you did this, but for example, you've got your "SelectionManager", now how is that going to be referenced by other things that need it? If you make it so that you can "pick up" something that you select, does the inventory talk to it, or does it talk to the inventory? Etc. This is the kinda stuff that people (myself included) get burnt out on when learning, and Unity is NOT very good at giving you recommendations for best practices.

  6. Interfaces are nice. A dev partner and I have been working on a simple 2D shmup for about 1.5 years now (we are taking our time). It came in handy for a lot of stuff. We have interfaces like IProjectile that make it so that all our different types of projectiles HAVE to have a damage value, speed, lifetime, etc etc etc. It also comes in handy when using Zenject, which we do 😉

  7. Wow. This is an excellent tutorial on the SOLID principles. The example you used was so relevant not just to game programming but programming in general. Well OOP anyway.

  8. Very good 🙂 as always. but what about DRY principle? I always wonder is this piece of my code, is small enough that can be repeated.

  9. Not that it is important to the lessons shown in this video but you could derive from Unitys IPointerEnterHandler and IPointerExitHandler interfaces (in the UnityEngine.EventSystems) instead of implementing your own raycaster. (Note that you do need to add a Physics Raycaster component to your Camera object for it to work, which I saw another comment mentioning but is actually not needed since the code in the video is doing its own raycast.)

  10. Great video, looking forward to more architecture related videos. You should do a series on Event architecture(delgates, events, actions, func) to decouple code.

  11. Nice tutorial and interesting use case. However am I the only one who think the naming is very unhelpful and hard to read. For example selection could be more specific like selectedObject or selectedTransform

    What I mean is that the fact you have to show at the beginning what the script does is already kind of absurd.

    I would rename this class content so it would be much easier to work with.
    the easiest way I see this is by:

    if(selectedObject == null)

    Why I think this is better is because
    if you read it without the boilerplate code it sounds more human.
    If object is null, Use object highlighter to highlight and give highlighter the object you want to highlight.

    The way its on video is like:
    if selection is null, selection response onselect and give the selection responder the selection.

    It doesn't tell much what is happening. you just can see that it does something and that is just sad. Its very cryptic and looks cool, but only makes other people have an headache by going through additional code. Imagine if unity would have been made this way. You would need to be able to see the source code behind api to use any of its features without help.

  12. Of course it is great, however in your code there is some misconception. The second implementation 'thinks' that something would have outline. So you can't just change behaviour without changing the other stuff. So this is bad. Sa for interfaces and so on.. In my opinion this is used rear in Unity, because the game designers change the game so fast so that writing interfaces will be your nightmare. Interfaces and such stuff only needed in some core features of the game which supposed to be stable in its rules. In general, it is a good tutorial to know the principles. My principle of coding: do something that need to be done where it should be done. =)

  13. Tip for clean code : event handlers should call a function, but not contain implementation. You want to have a quick look at it and understand what is going on, not read throguh the whole event.

  14. In this case, instead of interfaces, I'd use abstract classes that inherit from MonoBehaviour. That way you won't have to assume something IS A MonoBehaviour that IMPLEMENTS an interface. Also, some assertions to lock these invariants. Great video, nice editing.

  15. Interesting. But once you will watch few videos about optimization Unity games code you wont wan to use this principles. Basically you just much more limited in what you can use. Classes? Access to object on heap is 300 cycles long. The more of you functionality is modular in a way of using classes – the worst the situation is. If this is not enough – good old GC is here to make you fall. What is left? – Structures. The can inhere only interfaces. So the more flexible your game code – the more it is just prototype version. Personally I think that C++ is handling OOP much better. I have impression, that you better should learn ECS, because you don't wan't to know how it is really hard to optimize Unity C# MonoBechaviour code.

  16. It's great, but why not just use UnityEvents? This way things will still be nice and decoupled, but much less code (= time) required overall. With UnityEvents, in most cases you won't even need to write a script for a responder, just set what needs to be done directly in the inspector. This flexibility allows to change stuff very fast, which is important in game development.

  17. Fantastic tutorial. When working in the corporate world, I always try and teach the SOLID principles to developers from day one. There's nothing more demoralizing then coming across legacy code with a 12,000 line method in a class that does 37 things and nobody knows how it works. 😉

  18. In desperate need of something that shows your hotkey commands. Preferably the name of the command that's the hotkey is calling for those who have different hotkey setups in Rider.

  19. The only annoying thing here is that Unity can't serialize interfaces so you can't drag&drop them in the editor. That's why I usually create a abstract base class which also inherits from MonoBehaviour instead (obviously this only works if the class only implements a single "interface", because otherwise it couldn't inherit from multiple abstract classes).

    It's kinda ugly, but I don't want to lose the drag&drop workflow in the editor.

  20. Please make more videos like THIS. Do it again with example 2, then example 3. You got alot of views on this. If you check out your spatialOS videos….not many views…. We appreciate your skills as a coder more then you dabbling into the "new".

  21. What about putting any ISelectionResponse as a scriptable Object. Since its only being used by the SelectionManager. You know how an scene object can have alot of components attached to it. Instead of adding another one script to the object, you can drag the selection behavior onto the script using it. Is this a good, or bad idea?

  22. Great job! Quick question though, why don't you have a Rider warning for the serialized + private fields that don't begin with an underscore?

  23. Thanks for this. I will be using this in my game. I have a class that is equivalent to your selection manager that does way more than what your class did at the beginning of the video. I have a lot of refactoring to do 🙂

  24. Dude Youtube recommendation just nailed it, your tutorials are pro definitely, hey keep rocking new subs here I would like to see more stuff like that 🙂

  25. Could you help me with principle of single responsibility?
    Question link

Leave a Reply

Your email address will not be published. Required fields are marked *