Unity and C# Design patterns and concepts.
Bài đăng này đã không được cập nhật trong 3 năm
Unity is a really easy game engine to get started with, its interface is simple, it is filled with API that will help you build your game quicker than any other engines do, these reasons make Unity really popular and it is used to create many games.
But, as much as Unity is easy to get to, if you don't have a basic knowledge of programming patterns and concepts, it could be a nightmare in a later stage, making debugging your app hell. To avoid that, I would like to present you this little guide, hopefully it will help you in your future projects.
Singleton:
A very controversial design pattern, you should have heard or using it many times before. This design parttern restricts instances of a class to only one single object. Singleton usually is used to coordinate and manage functions, and only one object is needed.
Now if you wonder what are adventages of using Singleton pattern over static classes. Basically static classes are lazy-loaded when they are first referenced, and must have empty static constructor, this could lead to breaking the codes if you are not careful. For singleton pattern, it does a lot of stuffs, and static initialization method makes them immutable.
Furthermore, singleton can implement an interface, which static classes can't, allowing you to have a game object with components on it for better organization. Also you can inherit singleton from base classes.
However, you should be careful with using Singleton, as the name suggests, singletons work indepently, and should be not relying on other classes. If you are using singleton, make sure it can works on its own. Implementation of singleton:
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour {
protected static T instance;
static public bool isActive {
get {
return instance != null;
}
}
public static T Instance
{
get {
if (applicationIsQuitting)
{
return null;
}
if(instance == null)
{
instance = (T) FindObjectOfType(typeof(T));
if (instance == null)
{
GameObject go = new GameObject("Singleton-" + typeof(T).Name);
DontDestroyOnLoad(go);
instance = go.AddComponent<T>();
}
}
return instance;
}
}
void OnEnable()
{
if (FindObjectsOfType(typeof(T)).Length > 1)
{
for (int i = 0; i < FindObjectsOfType(typeof(T)).Length; i++)
{
if (FindObjectsOfType(typeof(T))[i] != this.gameObject.GetComponent<T>())
{
Destroy(((T)(FindObjectsOfType(typeof(T))[i])).gameObject);
}
}
}
}
private static bool applicationIsQuitting = false;
void OnApplicationQuit()
{
applicationIsQuitting = true;
instance = null;
}
}
Calling singletons:
Create your singleton script and inherit from the above class, and calls it using Instance
public class MyClass : MonoSingleton<MyClass>
Then you can call your singleton from any script in your scene, in example a function StartGame.
MyClass.Instance.StartGame();
Unity will try to find for your object on the scene, and if it can't find one, an instance will be created, it will not be destroyed until you quit your application. Unfortunately, there is no good way to remove the "Instance" keyword right there.
Pooling:
There are some cases when you have to instantiate an object multiple times, in example bullets shoot from a gun, this could put your application to extremely heavy loading, with each instantiate lowering the FPS count even more.
To prevent multiple instantiation, pooling technique is usually used, pooling is a resource management technique, allowing reusement of objects, which is important when you create and destroy a lot of them.
You basically generate a number of objects when the scene starts into so-called pool and hidden from plain sights, then when spawning that object, the object will be taken from the pool and put into use instead of instantiating a new one. This will minimize the risk of slowing your app by creating multiple new instances. Once the object has finished its works, it will be returned to the pool, awaiting further calls.
Following is a really nice Pool system that you can use for your application, all credits go to Sumit Das at SwiftFinger Games.
https://github.com/6ameDev/Advanced-Pooling-System
Delegates:
Delegate, as the name suggest, is a reference type of variables, it is holding the reference of methods with the same signature as the type it was declared with. What is beautiful about delegate is that it can be changed at runtime, when called, it notifies all the methods referencing by it.
To explain it better, delegates are like a list of functions, and when you call it, it will call the function referenced to it.
Let's start by creating a class with delegate and an event from it:
public class EventManager : MonoBehaviour
{
public delegate void ClickAction(GameObject g);
public static event ClickAction OnClicked;
}
The delegate called ClickAction passes a gameobject that we can use to define game object it came from. Events allow you to communicate from an object to listeners, it scales without effort and decouples the reaction from the action.
Now, we need to call the delegate and pass it our GameObject, this has been done through a Raycast. Whenever you call the delegate, it will detects your mouse click and run a function referenced by it, in this case the function will deactive the object.
void Update ()
{
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (Input.GetMouseButtonUp(0))
{
OnClicked += OnClick(this.gameObject);
}
}
}
void OnClick(GameObject g)
{
if (g == gameObject)
{
Debug.Log("Deactivating gameobject");
gameObject.SetActive(false);
}
}
That was simple, delegates will help simplify your code, without writing a ton of boolean statements to determine if event or action has happened.
Now these are actually pretty basic stuffs, but most of programmers coming into Unity might passing them concentrating on Unity's APIs, I'm pretty sure these info will help you with your works, and then until next time, happy coding!
All rights reserved