Unity3D SOLID Principles - Dependency Inversion Principle
Vložit
- čas přidán 29. 08. 2024
- Check out the Course: bit.ly/3i7lLtH
-------
See how to apply the Dependency Inversion principle to your projects (without using a DI container or framework). We'll go over a few samples and show yor you can split up your code, pass in dependencies, and have code that doesn't quickly become spaghetti :)
More Info: unity3d.college
Join the FB Group: unity3d.group
Great example, no one talks about solid principles or design patterns in unity. I follow you for a long time, you deserve much more subscribers.
Are you sharing every video ? If you dont, dont talk about he deserve a shit
Lukáš Vašek I don't use social media. Yes, I share videos but only with my friends.
clearly a crazy person^
Unfortunately you find out about the only in job interviews when it's too late
@@118andrey its just useful for job interviews
It's not "dependency injection" if you're deciding which implementation to use in Awake(). But I don't think DI is easy to fit into MonoBehaviors and shouldn't be shoe horned in, so I think you did very a great job just separating concerns using interfaces and SOs.
But DI is easy and very useful outside MonoBehaviors as part of a larger set of infrastructure classes!
Great video, man. This series is really helping me learn Unity.
Sir can i use zenject framework for DI in unity... Or something else will be more beneficial or just manual IOC, pls give a light on it.
Thanks in advance... ❤️
How would you recommend applying depency inversion in unity now?
Unity3D College is a hidden and precious gem.
Thank you for sharing your knowledge and experience!
Holy shit, this guy is good. More Unity devs should know about this channel. Let's spread the word!
Every Second of these SOLID videos are worth their weight in solid gold. I know it was impossible for time to have weight, but that changed when these videos were posted.
Dependency Injection is NOT Inversion of Control. For IoC this is a very Well done tutorial. For DI (Dependency Injection) just use Zenject / Extenject project.
You're the best unity teacher i've ever seen. So clear.
Short, condensed, and just crammed with _great_ information about lots of good practices.
This made the coin drop for me with scriptable objects. Didn't feel like I got it quite right before when trying to use them but now I'm all set to do it properly!
Keep up the amazing work!
He has put aside components in unity!! Is it fantastic?! Absolutely no. If you want to access ShipInput or ShipMotor outside of the Ship script, what do you do? You need GetComponent instead of GetComponent for example!
thank you so much for making these videos. I made a few games by watching unity tutorials and I'm trying to extend them into something that I can actually release and would be fun to play. I'm running into some limitations of the way the tutorials designed a lot of the objects in relationships and your videos are giving me some fantastic ideas on how to rewrite the code using solid design principles.
YOU are a Legend! Watched a lot of videos to grasp SOLID. But, your example of Liskov Substitution was the best one. Big Thanks!
Please be aware that Dependency Inversion and Dependency Injection are not the same thing. In this tutorial you're talking about Dependency Injection. Dependency Inversion is when a high level class is depending on the abstraction of a low level class rather than depending on that low level class itself. Dependency Injection on the other hand is when a class is injected with whatever object it needs to operate on, so it doesn't need to instantiate that object within itself .
Either way, this is neither DI. This is just SOC based on settings and interfaces, which is good too.
@@christobanistan8887 What does SOC stands for?
@@soonfamous SoC - separation of concern
Thanks for the vids guy, most tutorials tell you how to do something but you tell us why to do it. Much more beneficial to an intermediate noob like me who understands basic methods, syntax, variables, etc. but is expanding my small project and quickly getting lost in my mess of code.
Thank you very much Jason, for providing SOLID that has really SOLID EXAMPLE as well!
May the force be with you!
Frankly I don't exactly know just yet how I will apply this principle to my current projects, but I can definitely see the benefits of all this separation!
Thank you for your clean and useful example
Great video! Came here after five other videos on DIP and this is the one that made it click for me!
Thank you for the complete and concise explanation of this principle.
I just found this channel.
Solid work sir, thank you so much.
Hello Jason, thank you a lot, your courses are helping a lot to understand the logic behind making video-games !
[RequireComponemt] is a great way to enforce dependency binding at compile time as well!
thank u so much jason, please share the video on social media platform he deserve a lot
Amazing example! We always heard about dependency injections and IoC, but I've never seen before these patterns being explained using Unity.
The implementation in Unity is a little different (and maybe controversial), but the principle is the same independent of framework.
But I want to comment about Ship class: Instead passing these information on ShipMotor constructor, I'd create a struct who handle it, and would pass the instance as param, something like this:
public struct ShipDependences{
public ShipSetting shipSettings;
public IShipInput shipInput;
public Transform transformToMove;
}
Ship class:
private ShipDependences shipDep;
private void Awake(){
shipDep = new shipDependences();
shipDep.shipSettings = this.shipSettings;
shipDep.shipInput = // The same code from video
shipDep.transformToMove = this.transform;
shipMotor = new ShipMotor(shipDep);
}
Thanks for all your tutorials. They are really helpful. Could you cover the unit (or even integration) testing in unity in the future videos? It would be very interesting and consistent with SOLID principles.
Nice work! Composition over inheritance is the best thing in OOP :)
Wow, gave me a new perspective on everything - really wish I found this sooner.
Thank you for the great video and explanation.
Good vocal balancing man! Great tutorials!
The D on SOLID is not about Dependency Injection, it is about Dependency Inversion. But I liked your explanation about Dependency Injection!
Unity is one big dependency injection engine, isn't it? That's the whole point of the properties - unity is the service object layer defining the makeup of dependency injections into the monobehaviours, and the monobehaviours being equally injections into the components being placed in the hierarchy.
From looking at a few other descriptions on dependency inversion, it's essentially the idea that intercommunicating classes have interfaces that they communicate through, so that you are never relying on concrete implementations directly, such that you can change out the concrete implementation class. So actually your substitution video was probably closer to it where your attackMelee function used two interfaces of intercommunicating classes to perform a complementary task with them. Maybe adapter pattern is the easiest explanation!
Also, the third example broke another handy facility which is being able to dynamically unload the AI while the game is running.
Thx you, this tutorial was really helpful for me. Can you do more Profesional tutorials like this ? I mean using patterns and good programic convension in Unity ;).
What if we directly used a version of Tick ( ) with parameters instead of cache variables ?, something like Tick ( IInput, Tranform, Shipsetings ) that apparently would have the same effect without having to instantiate the object on Ship class.
Thank you for these videos. I'm a huge advocate for clean, testable code and these videos are exactly what I've been looking for as I develop projects in Unity.
I have a quick question: is there a strong reason you chose to implement IShipInput as an interface and not a base class? It seems to me like using a base class would make more sense in this example since the functionalities of "ship input" would be specific to the ship.
Thank you very much for your very useful and awesome videos!
As Game Dev and Design student, they should be teaching stuff like this...
great series of videos.
SOLID explanation :)
thank you!
Please could you provide the project in the description? That would be really helpful for me to pick apart.
Here ya go - unity3dcollege.blob.core.windows.net/site/YTDownloads/Dependency%20Inversion.zip
Thank you very very much man
Awesome, thanks for the great info.
Second one seems easy enough, third one is mad!
You are digging so deep. You are really have kind of missing manual channel) So you are from NASA) That explains a lot)
Thanks
Hey jason that implementation is similar to Proxy Pattern right? or I am wrong.... I mean Unity has differnte approach in IOC , less generic thatn the IoC ...and Factory method is DI ... so if Ioc is more generic implementaion of DI:.. Unity provides by Awake a fake constructor? so it is a Lightway Ioc?
Awesome channel! :)
Hi Jason, thanks for the video! I am in search of good ways to implement DI principle in my projects and have read some articles explaining how people do this. And I also made a research of main defference between interface and abstract classes, which seems to boil down to the rule: abstract classes are used to unify related classes while interfaces do the same but for separate classes.
I was always interested what is the profit of using interfaces compared to abstract classes in order to follow DI principle?
In your video we see that you made an abstraction on ShipInput, which is great, but why you didn't use an abstract class like AShipInput with ControllerInput and AIInput as its children?
This seems to make sence because both ConreollerInput and AIInput represent the same purpose - working with input. From my perspective it is worth to gather such classes under parent abstract class, not interface.
The same question comes from the code review you've made recently czcams.com/video/djLU7_SVZ8U/video.html#t=6m. There's also an abstraction of input and it was implemented using interfaces, while I would go for an abstract class.
What am I missing? Could you please elaborate the choice of using the interface approach?
Interfaces share Behaviors. Classes (objects) share function and structure.
A better way to think about interfaces is that they provide a signature of behaviors, but provide no promise of function/structure. This provides a high level of abstraction and decouples the two regions of code.
This is why I am not a fan of abstract methods. They split the difference on interfaces and strong classes creating a very weak class that doesnt promise function nor provides reliable abstraction.
I'm trying to apply these principles, but sometimes it just feels silly. Like I've vastly expanded the number of classes I have and it would seem far more difficult now to show someone my scripts and have them understand it. And for what? So if I decide to add some feature later it might be easier? Wouldn't planning be more effective?
Is there a justifiable reason to apply this principle to a small solo project? I'm trying to, but I just feel like a clown adding all these interfaces and new classes to create what was previously a pretty simple single Monobehaviour, though one that violated SOLID principles.
Thanks for the idea. :)
Interface ! yess! :)
I have heard that Dependency Injection is Dependency Inversion, but Dependency Inversion is not necessarily Dependency Injection.
Oh well, I dont know if you know this, but I think I've read on some efficiency Manuals on Unity that the "? :" operators is inefficient.
This is really helpful but It would be much easier to follow if you offered the project/source code for download
Excelent video and example, thank you again. Would you upload the code?
Very helpful ! I have a question. I see that we can do property injection basically using the scriptable object drag drop method. However, what if I property inject via the editor an implementation of (lets say) IDoSomethingService on multiple monobehaviors across multiple scenes. And now I wish to replace it with another concrete implementation across my scenes. I would need to trace manually through every monobehvior that consumes the old implementation and change it. The traditional DI allowed a bootstrapmethod in which I could change one line to hook up the new concrete implementation. This is because in the traditional DI the container acts like a factory which is another layer of abstraction between the concrete implementation instance supplied and the consumer of that instance. In case of using unity editor we lose this abstraction leading to the problem i just mentioned. Do you have a solution for how I could switch out a concrete implementation when using unity editor property injection. Thanks !
Is there any way I can download this project!!
Great Explaination
Isn't it an MVC pattern?
Sorry, might be a stupid question.
What is the benefit of this? Wouldn't you want to keep your different components as Monobehaviors so that you can interact with them through the inspector?
You can and should keep some things as Monobehavior or as substitute for MB. This is a very good method once you are outside the area of the editor, such as with services (which are really hard to MB out once you pass the base object) or with data created objects such as if you build prefabs on the fly.
With the ScriptableObject you can interact with them through the inspector.
new AiInput() as IShipInput, can you please explain the syntax of this statement?
Jason replied to same question by neevir below. I thought at first it was because ShipMotor constructor was expecting an IShipInput interface so it was casts to that.
Hey Jason, I didn't know you had a channel. :)
Subd.
What's the meaning of "as IShipInput" on line 13 at 7:00 ?
Casting. Like x =(IShipInput)y. But i think it no need for this case.
It's just there so the ? operator will work . Without casting it, it doesn't know that we want to use it as an IShipInput so it gives an error because the 2 types that implement it are different. If it weren't for that operator, it wouldn't be needed.
yeah, i mistaked.
@@Unity3dCollege Another reason to avoid the ? : operator.
interfaces always confuse the shit out of me.
If i had to do this, id probably create a generic Ship.cs script which has public Move(var thrust, var direction), then id have 2 scripts called PlayerControl.cs and AIControl.cs, and whichever one is added as a component will take control of the ship.
and if the game is really really simple, id just a have a boolean toggle in the Ship.cs script itself and eat the cost of If statement checking the boolean before movement.
unless theres a way to put methods inside an array, so i can set a value to 0(player), 1(AI) in Awake() based on inspector toggle, and have an array containing playermove() in index[0], aimove in index[1], and then just call it in update() without if statements.
It becomes a bit more apparent when you start intermingling systems. Having a system that deals damage to a Ship vs one that takes an ITakeDamage interface can make a big difference. I'll have to do a few more videos on this though to really explain it I think :)
Jason, thanks for your great videos. I put all your code in my project which is a top down shooter. The movement from the player and the enemy works (nearly) fine but the control of the player is like running on ice. The character is not stopping exactly after key is up. He slides a little bit in the move direction. May be you or someone else can help.
public class CharacterMovement
{
private readonly ICharacterInput characterInput;
private readonly Transform transformToMove;
private readonly CharacterSettings characterSettings;
public CharacterMovement(ICharacterInput characterInput, Transform transformToMove, CharacterSettings characterSettings)
{
this.characterInput = characterInput;
this.transformToMove = transformToMove;
this.characterSettings = characterSettings;
}
public void MoveTick()
{
Vector3 movement = new Vector3(characterInput.Horizontal, characterInput.Vertical).normalized;
transformToMove.position += movement * Time.deltaTime * characterSettings.MoveSpeed;
}
}
public class ControllerInput : ICharacterInput
{
public void ReadInput()
{
Horizontal = Input.GetAxis("Horizontal");
Vertical = Input.GetAxis("Vertical");
}
public float Horizontal { get; private set; }
public float Vertical { get; private set; }
}
public class Character : MonoBehaviour
{
[SerializeField] private CharacterSettings characterSettings;
private ICharacterInput characterInput;
private CharacterMovement characterMovement;
private void Awake()
{
characterInput = characterSettings.UseAi ?
new AiInput() as ICharacterInput :
//if not
new ControllerInput();
characterMovement = new CharacterMovement(characterInput, transform, characterSettings);
}
private void Update()
{
//Input for movement
characterInput.ReadInput();
characterMovement.MoveTick();
}
}
Hate solid principles, just make everything more complicated and the game runs just fine without them, just useful to know for interviews
this doesn't seem very clean either, sure your code is cleaner, but your dependencies and your editor can quickly become a mess.
Sorry but it was awful. Unity uses components.You put aside components because of using interfaces!! You should utilize components that implement interfaces. Components are so useful that it is not required to explain but you throw away them.
Sorry, but for me, this code increases the maintainability, scalability, update facility, usability, and export capacity of the entire project.
this does great for me until I need complex data storage services, specifically easycaching.hybrid and dapper with redis bus lol