I've been writing unity games for some time now using C#. After each game I became more and more experienced, my code changed, I started using best practices.
However, today I have a question: how to build the application architecture correctly?
I don't like that there are a lot of fields in my code that are mixed together with the main logic, I feel that this should not be the case. The solution I have come to so far is to make 2 classes, one contains all the information, and the second implements all the logic, but the class in which all the logic is located becomes dependent on the class with information.
Tell me, more experienced colleagues, what is the right thing to do?
CodePudding user response:
Each project has it's their own needs and the architecture will change based on those needs.
The starting point should be SOLID Principles for you, because many things based on those principles like IoC which it's based on Dependency Inversion principle.
Then later you could use design patterns to apply those principles for your needs.
After getting familiar with design patterns you can look at some already written frameworks like Strange(IoC MVCS) and Entitas(ECS).
CodePudding user response:
To begin with, try to divide the class that contains logic into smaller parts so that every part only has one responsibility and does one particular thing. Then move these parts into other classes. Try to find a balance when splitting the logic class. Making new class for each method is one extreme, having one class with all the logic is another extreme, the solution is somewhere in the middle. When it's done move on to the next step.
The next step is to name these classes. It might seem easy, but it's really important. Some good examples of naming:
- PlayerMover or PlayerMovement, the class which responsibility is to move the player in the chosen direction.
- PlayerInput, the class which responsibility is to interact with input and translate it into the language that other components can understand. For example translating keyboard input into Vector3, so PlayerMovement doesn't have to worry about which key was pressed, it only knows where to move, so their responsibilities don't intercept more than necessary.
Tips on naming:
- Classes represent entities, so they should be named as entities, it means that their names should be nouns
- If giving a name to a class seems hard then the class is most likely formed wrong and has too many responsibilities or doesn't have a whole one
The next step is to separate different layers. Try to make logic independent of UI, so UI can be changed or removed without affecting logic layer. Continuing with the example of player subsystems, make PlayerMovement independent of type of input it uses with abstractions if needed, so it can be keyboard input or joystick input and PlayerMovement doesn't care which one. Also make PlayerInput independent of whether someone uses it or not, for example with properties or events. It will allow to create components once and then use in any project without rewriting everything.
Talking about dividing data from logic, it'll most likely result in having two very similar inheritance hierarchies, so it's better to store the data right where it's used, unlike it's a special case with settings file or big amounts of data.
These are basic tips on this topic and of course building a project architecture is much more complicated than that, but these are great things to start with.
You can continue with Solid (some principles are already mentioned here) and things like Zenject