What are the best practices of designing code architecture?
Hello all, First of all I would like to state that I will maintain this topic as long as I can. I have intention to make this question a collection of up-to-date guidelines/resources that people can benefit from. The reason I'm not using Forum is people tend not to look forum posts and prefer to go with UA. If this post becomes too big, I'll create a forum post and give a link to it here.
Now to the question, what is you own way to maintain your code base/project along with your team that will prevent it to be a complete mess on a large scale ? Or what needs to be done to be experienced in creating/maintaining a code base that will not be a mess on a large scale ?
In order for everyone to benefit from your answer I recommend you to keep it as simple as possible and explain hows/whys of the reason. I will try to update this to make it easier to read. And try not to give theoretical or general answers like "separate your game logic and you will be fine" try to be specific, provide sources and/or sample code.
Thanks.
According to my findings;
Design Patterns
These are some generic code patterns that will help solve some specific problems that has been recognized in programming. For me, you should at least know what they do. You should also know that design patterns are not voodoo magic, they solve some problems but they may have some limitations or create other problems.
A good source that explains design patterns in C# [http://www.oodesign.com/]
Explains game programming patterns(unfortunately in c++ but it's very explanatory) [http://gameprogrammingpatterns.com/]
Some design patterns applied in unity [http://www.habrador.com/tutorials/programming-patterns/]
Model-View-Controller(MVC) and it's Variations
Very simply put, it's like seperating your project in to three parts. Model is only responsible from data part(CRUD)and nothing else(i.e. increasing/decreasing enemy health), View is responsible from representation(i.e. position of enemy, enemy health bar HUD), lastly Controller is the middle-man between Model and View. Passes information from Model to View and vice versa. Now the reason that I've written "it's Variations" in title is that I've seen many arguments about unity being a "component-based" engine and an MVC architecture falls into Object Oriented Programming style. And a lot of people disagrees with this notion with having strong reasons to disagree, you can look into it but nevertheless you should learn about MVC. There are lots of sources in web that explains MVC in not game-engine specific ways, you can look it up if you like but I will only give engine-specific sources.
Good explanation of problem(s) and why you need MVC-like architecture(a variation of MVC). [https://www.toptal.com/unity-unity3d/unity-with-mvc-how-to-level-up-your-game-development]
A bit more advanced version which is really good in my opinion. [http://jacksondunstan.com/articles/3092]
Note that MVC is also a framework but the reason I separated it from "Frameworks" section is that MVC is a starting point to the things I will list in "Frameworks" section.
Frameworks
Some of previous things + bunch of other things = framework. Frameworks are prepared to make things more standardized and clean with principles like Inversion of Control(IoC), Dependency Injection(DI) etc. If you don't know these terms, I suggest you to look them up.
Entitas: Actively updated and maintained, free, Open-Source Entity-Component System Framework, created by Wooga[https://github.com/sschmid/Entitas-CSharp] also in Asset Store which will include more features in future but costs 45$ [https://www.assetstore.unity3d.com/en/#!/content/87638]
StrangeIoC: Free, Open-Source IoC framework, a good one but not updated for a long time [https://github.com/strangeioc/strangeioc] also in Asset Store [https://www.assetstore.unity3d.com/en/#!/content/9267]
Zenject: Zenject is a lightweight highly performant dependency injection framework. Up to date and very performant. [https://github.com/modesttree/Zenject]
uFrame : MVVM Framework that comes with cool features like diagram engine but not maintained for a long time and I'm not sure if it works with Unity 5.6 https://github.com/InvertGames/uFrame-MVVM] also in Asset Store [https://www.assetstore.unity3d.com/en/#!/content/14381]
Probably one of the most important things is notations and summaries. I personally don't do this in my own projects, but that could be in part due to laziness. However, when reading someone elses code, it is very important to know what they were thinking at the time. No amount of standards and design patterns are going to make shotty code readable.
Secondly, the $$anonymous$$VC thing is relatively new in the software industry, and it is not a bad design pattern, but to effectively do that in game design, you would be looking at something like this.
public class Player$$anonymous$$odel
{
public PlayerController controller;
private int _health;
private int _maxHealth;
public int GetHealth()
{
return _health;
}
public int Get$$anonymous$$axHealth()
{
return _maxHealth;
}
public void Remove(int amount)
{
if (_health -= amount < 1)
{
controller.PlayerDies();
_health = 0;
}
controller.UpdateHealth();
}
}
then
public class PlayerController
{
public Player$$anonymous$$odel player$$anonymous$$odel;
public PlayerView playerView;
public void AdjustHealth(int amount)
{
player$$anonymous$$odel.RemoveHealth(amount);
}
public void UpdateHealth()
{
playerView.SetHealthBar($$anonymous$$athf.InverseLerp(0, player$$anonymous$$odel.Get$$anonymous$$axHealth(), player$$anonymous$$odel.GetHealth()));
}
}
and
public class PlayerView
{
public Image HealthBar;
SetHealthBar(float percent)
{
HealthBar.fillAmount = percent;
}
}
As you can see, this does indeed work nicely and look clean, but is it actually necessary? No. It is usually used in large companies for when the code structure is so large, that no single person knows every file. So they split every concept into 3 ideas so that editing it becomes easier. However, I personally prefer $$anonymous$$odel-ViewControl, meaning the $$anonymous$$odel is the data, and the ViewControl is the view and the controller. It might be personal preference, but a lot of the coding design patterns are meant for large companies to keep things organized, in a smaller company this sort of organization can actually do the reverse, and over-complicate what may have been a simple thing to begin with.
Hey @RobAnthem, thank you for your contribution. I agree about notions and summaries, but I'd like to make an addition here. You will be a stranger to your own code after 1 week. I personally try to make it a practice of "if you can't read the code like English, add comments". Sometimes it might feel redundant to add comments, but when you done working with that class and you came back to that class after couple of days, it will take a lot more of your time than commenting it. Also sometimes you do complicated things and might get confused. Commenting it greatly helps you grasp what's going on exactly.
And about $$anonymous$$VC, most of the things I wrote up there is mainly focused on keeping things clean and separated $$anonymous$$m-wise so people will not interfere with each other. But also, $$anonymous$$VC can help you store your codes for later use. Perhaps you would like to make a series of games that are about, lets say idle clickers. You want different implementations in each game but base mentality is the same. If you work with $$anonymous$$VC this way, you will save a lot of time. Or, you want to have an inventory system in the game, if you make it as generic as possible, you could use it in any project that requires inventory system(who doesn't need a good inventory system, right?).
I think it is necessary for a lot of developers to learn this kind of stuff if they would like to work in this industry in a professional scale.
before i comment, I will say this is place for people to get specific answers to specific questions. and i'm surprised this question got past our moderation. but since it did, I will through my honest opinions out here for once. Contrary to other types of program$$anonymous$$g or wherever your program$$anonymous$$g background might be from, I believe coding in game development is an art form. No different than the people creating music and meshes and textures and concepts for a game. coding formats should allow for creative systems and formats in coding. originally is the only thing that keeps the indie game industry alive among the larger companies. if you are working with other programmers my advise is to use as many comments in your code as you have to. unless you are relying on asset stores. at somepoint you will be forced out of the box to achieve a full featured game. video game program$$anonymous$$g is just different than other program$$anonymous$$g like that. I will say many programmer personalitys might disagree with me on this opinion. but i am good at what i do and thought i would share my style.
While I do agree that game program$$anonymous$$g is a lot more of an art than basic program$$anonymous$$g, as we are not only solving more complex issues than software developers, but we are creating these complex issues as we go. Also I'd like to put my two cents in here, I don't think anyone should be making games if they either A. aren't a programmer, or B. don't have one on their $$anonymous$$m. No amount of prepackaged solutions will give you what you want, and worse yet, the more pre-made code assets you purchase, the harder it becomes to integrate them. Hell I remember when I first started in this industry, and thought I could do it without coding, I spent a lot of money on assets that I now consider to be useless pieces of garbage. Frameworks, project templates, and so many other things that I would rather just code myself. I know a lot of devs say that you should always search for libraries that will cover aspects of what you are doing so you don't have to, but the way I see it, if it takes me close to the amount of time to write the library as it does for me to learn/integrate it, then I should just write it. So I really only use libraries like $$anonymous$$ath, Noise, Networking, etc, and would rather make my own for everything else. As I'm sure you are aware @toddisarockstar once you understood program$$anonymous$$g as a whole, creating any kind of solution is relatively easy. I understand peoples desire to want an easy solution, and it is of course more cost effective to spend $100 on a framework that may take a dev like us to build in a week or two, but as we all know, you will quickly find that framework doesn't do everything you want it to, and trying to add implementations onto it may be much more effort than you want. As well, it screws the entire "coding standard" if the pre-packaged asset is using a different standard. Personally I like the C# standards, and I really wish Unity devs would stop using the C++ standards of using _variableName and m_variableName, mostly because they are ugly to look at lol. I like my art to be appealing :P
Here is a link to Insomniac games core coding standards, something that is generally useful for game development, and just development in general:
https://gist.github.com/$$anonymous$$erollmops/fcad27cfef9e3552cb75a3d201494ba6
hopefully you find it useful.
Answer by FM-Productions · May 21, 2017 at 03:45 PM
Hi,
only a few points from my side (not just Unity specific, but for coding projects in general).
If you work in a team, take a few minutes at the start of the project to decide on common code conventions
Use a source control tool (like Git) for managing your Code (maybe not the binary data and .meta files from Unity - I don't know what a good source control approach for binary files would be)
Keep your code readable and understandable. That means for example, that a function should only do what the name is indicating. Have clear conventions for variable names etc. Avoid to make many comments. It is very likely that if you or someone else of your team is rewriting a function, they will not update the comment describing the function etc. If possible, express what your code is doing via the variable and function names.
Write tests for your code: Now I do not have any experience for Unity specific tests. But if you have static functions or regular classes, for example, it should not be hard to write some Unit tests for them. This way you can ensure that you see it immediately if some new code breaks your current code if you execute the tests.
Have as few dependencies as possible in a class. Communicate with other classes over interfaces. Let me explain: Say for example you have a class "CustomPlayer" in your project. Many other classes store a CustomPlayer instance and maybe call a function on the class or retrieve values from the class. If later in the development, you decide to change or rewrite your CustomPlayer class because some features you planned would not work with the current implementation of the CustomPlayer class, the changes are high that you break the code of other classes that are referencing the CustomPlayer class. Use an interface instead, so that you have no hard dependencies to the CustomPlayer class. Also, design patterns like Dependency injection and interface driven design makes it much easier to test your classes (Test frameworks allow you to write a "mock" that is used as referenced interface of a class you are testing. You can tell the mock how to behave when the test class calls specific functions on the mocked interface. For example, what return value should a function have if you call it with certain argument values). If you have no interface driven design and many dependencies, it would be pretty hard to test your code I suppose.
I agree with separating data classes from functionality classes.
A good book for writing maintainable code is "Clean Code: A Handbook of Agile Software Craftsmanship" by Robert C. Martin. The examples in the book are written in Java, so they should not be too hard to understand if you already know how to code in C#
Cool input, thanks! Didn't look at that angle about commenting codes, makes sense. Also it would be great for new people here if you could provide an example about the importance of using interface.
Also for Unit Testing, a lot people doesn't know Unity Test Tools. It's a good tool to make unit tests.
About using Interfaces, I stated in my answer that it is beneficial for a class to have as few direct dependencies to other classes as possible. Using interfaces as suggested by the Dependency Injection design pattern is also very important if you want to write Unit tests for your classes.
I looked up a quick example that gives an introduction on Dependency Injection. This may not have the best explanation, but it shows the concept with C# code: https://msdn.microsoft.com/en-us/library/hh323705(v=vs.100).aspx
Also, I would like to recite the section abou the usefulness of DI (dependency injection):
"Even after a demonstration of how to use DI, you still might wonder why it is useful. Two answers are that it is useful for unit testing, validation and exception management.
Unit Testing. Unit testing, or white box testing, tests a unit of work (often, this is a method). The assumption is that the developer writes the tests because it is the developer who knows the implementation details of what is being tested. A proper unit test can run without any reliance on external dependencies. DI enables you to replace complex dependencies, such as databases, with mocked implementations of those dependencies. This allows you to completely isolate the code that is being testing. For more information about unit testing in WCF, see www.microsoft.com. Validation/Exception $$anonymous$$anagement. DI allows you to inject additional code between the dependencies. For example, it is possible to inject exception management logic or validation logic, which means that the developer no longer needs to write this logic for every class. For more information about how to use DI for validation and exception management, see www.microsoft.com."
Answer by McLoud · May 24, 2017 at 07:40 AM
Also take a look to Entity-Component-System and use class diagram to design the code. I Like NCode, wich is free.
Answer by Nocktion · Feb 01, 2019 at 09:23 PM
I use something similar to MVC and ECS. I created a system that uses three types of classes.
Data
Data classes contain pure data and they are mostly ScriptableObjects to make simple presets (e. G.: items, world generation presets, etc..). The other type of Data class inherits directly from object and their primary purpose is to make instances of scriptable objects with additional functionalities and methods.
Functionality
Functionality classes do all the calculations and contain all the functionalities that your project will include. However it does not interact with "physical" parts of the system. It's entirely for handling functionalities.
PhysRep (Physical Representation)
PhysRep classes link functionalities with GameObjects. Theese are the only class types in the system that inherits from MonoBehaviour and therefore must be added to GameObjects as Components. They contain a few static variables, so you will be able to handle a few things from Functionality classes. These PhysRep classes are mostly singletons.
So this is my system design that works pretty well. It organizes the codes and also has some performance benefits.
Sounds actually very interesting. Do you have any link to a repo with an example? Would love to see it in action. @Nocktion