The Unity3D API provides many functions, but many processes will still be encapsulated by themselves. Of course, there are many frameworks available for downloading and using on the Internet now, but they will definitely not be easier to use than what you write.
Regarding whether the framework is needed, I am positive about whether to use it. I encapsulate some common methods and make it into a functional framework, which can greatly improve the efficiency of the code and facilitate maintenance.
For the "Game General MVC Framework" used in many tutorials on the Internet, it now does not conform to the design idea of a structural framework like MVC: You must know that MVC was originally designed as a framework for web applications, and many events in the game did not occur through user clicks on the UI. View and Controller generally account for a very small proportion of game logic. In many tutorials, many "Manager" modules were stripped of Model. Some people even combined View and Controller to write UIManager - even the structure of MVC is gone, why is it called MVC framework?
MVC: "A person is popular and has many troubles...."
Currently, most game frameworks - especially those of small projects - encapsulate the specific behavior of some data: generate an object, play a special effect, perform a random event, etc. Of course, there will also be some structural designs or resource management designs such as: UI fallback stack or fallback chain, scene loading records and switching, download queue management, etc.
In Unity's framework design, there is a word that is often seen: singleton. Singleton mode is to use only one instance of a certain class in the entire game. The core sentence is public static T Instance; that is, a static instance is defined in the class for external use, and the method is called: (). When I first came into contact with this design method, I often confused with static classes, (). There is a static Instance in the middle, and it seems that there is little difference in many cases. . .
For nearly two weeks, I have been struggling with whether the framework I am writing should be written as a singleton pattern or a static pattern. Today I happened to have a new idea about this problem: Can static be understood as a very closed singleton?
First, let’s recall two common senses about static:
1. Static classes cannot be inherited or inherited! (Strictly speaking, it can only be inherited) In other words, your static class cannot inherit MonoBehaviour and cannot implement interfaces.
2. Static methods cannot use non-static members! If you use static methods in large quantities and the methods need to use members of this class, then your members must be static members.
Note: If you want to adjust a parameter under the Unity editor, then this parameter cannot be static (even if you customize EditorWindow to modify this value is useless). The solution is to store the configuration (generate the *.asset file), then load it through LoadAsset during operation, and then change the static members. As for the reason, I believe it is not difficult to understand - all the Unity components you see are instances. If you want to configure them through the Unity editor, you must have such a configurable instance.
Think about it from an object-oriented perspective: a static method or a static class does not need to depend on an object, the class is unique; a static instance of a singleton is generally the only object (of course there can be multiple). What a difference. . . It doesn't seem to be big. . .
If this is not wrong, then look back and compare the two ways:
1. Static (static methods or static classes), a stumbling problem in code writing, method calls are very convenient, and the operation efficiency is much higher. Logic is process-oriented and does not have good control over loading and destruction.
2. Singleton (static instance of class), code writing is exactly the same as other classes, and it can inherit the abstract template interface. It is also very convenient to configure parameters in Unity, but there is a possibility of making mistakes when using it (the method must be called through the instance), and the efficiency is not as good as static (but it will not have a big impact).
If these statements are too abstract, then I'll give another common question: If your framework has a SoundManager that can manage all sound playback, then how would you implement it?
(When I first came into contact with the AudioSource component, I thought that every sound would be played by an AudioSource. But later I found that it was completely unnecessary. AudioSource has a static PlayClipAtPoint method to play temporary 3D sound effects, and there is an instance method PlayOneShot to play temporary sound effects (2D and 3D depend on the SpatialBlend of the instance). If there is no special requirement, then an AudioSource will play background music loops, and the above two methods will play special effects audio in the game, which is enough for most games.)
Then the question is: If your SoundManager's method of playing sound is static, then the AudioSource component must be retrieved in the code in various ways (create a new component or obtain a component under a specific GameObject) - because the variables that hold this component must be static, so it cannot be assigned through the Unity editor. If you don't read the code, the user has no idea what kind of component acquisition process this is. If I destroy this process (objects with the same name, including mutex components, etc.), then this Manager is likely to experience unpredictable exceptions.
Inheriting MonoBehaviour and RequireComponent(typeof(AudioSource)) is much more convenient and robust than "static for the sake of static".
In fact, at this point, we can basically summarize when we need to use singletons:
1. As long as your class needs to save other components as variables, it is necessary to use a singleton;
2. As long as you have the need to configure parameters on the Unity editor, it is necessary to use a singleton;
3. As long as your manager needs to control the loading order, it is necessary to use a singleton (such as loading ResourcesManager after hot updates);
Of course, it is just "necessary" here, not "necessary". The biggest difference between the two is that one is easy to write and the other is easy to use. The price of convenient writing is to add an instance for each call, and the price of convenient use is to give up the "what you see is what you get" of object-oriented or Unity. Which one is more important, you can decide for yourself.
On the other hand, like "static for static", "single for singleton" is also an unreasonable design. Such an explanation is still so vague, so define the simplest rule for yourself - if there are no variables in your singleton class that need to save state, then the methods in this class can be all static methods, and this class can also be a static class.
Supplement: Starting from instances, understand singleton patterns and static blocks
Even if you have not used other design patterns, you must have been exposed to singleton mode. For example, the default bean in Spring is singleton mode, and all instances using this bean are actually the same.
Singleton mode usage scenarios
What is singleton mode? Singleton mode is also called singleton mode. Its purpose is to ensure that a class has only one instance in the system and provide a global access point to access it. From this point of view, it can be seen that the singleton pattern appears to ensure that there is only one instance of a class in the system and that the instance is easy to access by the outside world, thereby facilitating the control of the number of instances and saving system resources.
There are certainly reasons and benefits to use singleton mode. Singleton mode is suitable in the following scenarios:
1. There are frequent instantiation and then destruction, that is, frequent new objects, and the singleton pattern can be considered;
2. Objects that take too much time or too much resource when creating objects, but are often used;
3. Frequently access objects of IO resources, such as database connection pools or access local files;
Here are a few examples to illustrate:
1. Statistics on the number of people online on the website;
In fact, it is a global counter, which means that the number of online users obtained by all users at the same time is the same. To achieve this requirement, the counter must be globally unique, which can be implemented in singleton mode. Of course, distributed scenarios are not included here, because counting is in memory and thread safety must be ensured. The following code is a simple counter implementation.
public class Counter { private static class CounterHolder{ private static final Counter counter = new Counter(); } private Counter(){ ("init..."); } public static final Counter getInstance(){ return ; } private AtomicLong online = new AtomicLong(); public long getOnline(){ return (); } public long add(){ return (); } }
2. Configuration file access class;
Projects often require some environment-related configuration files, such as SMS notification-related and email-related. For example, for properties file, here we take reading a property file configuration as an example. If you are using Spring, you can use the @PropertySource annotation to implement it, and the default is singleton mode. If you don't use a singleton, you have to have a new object every time and read the configuration file again every time, which will greatly affect performance. If you use a singleton mode, you only need to read it once. The following is a simple implementation of file access singleton classes:
public class SingleProperty { private static Properties prop; private static class SinglePropertyHolder{ private static final SingleProperty singleProperty = new SingleProperty(); } /** * Content is =kite */ private SingleProperty(){ ("Constructor Execution"); prop = new Properties(); InputStream stream = () .getResourceAsStream(""); try { (new InputStreamReader(stream, "utf-8")); } catch (IOException e) { (); } } public static SingleProperty getInstance(){ return ; } public String getName(){ return ("").toString(); } public static void main(String[] args){ SingleProperty singleProperty = (); (()); } }
3. The implementation of database connection pool also includes thread pool.
Why pooling is because it is time-consuming to create new connections. If you create new connections every time a new task comes, it will have a great impact on performance. Therefore, the general approach is to maintain a connection pool in an application, so that when a task comes in, if there are free connections, it can be used directly, saving the cost of initialization.
Therefore, using singleton mode, it is possible to realize that there is only one thread pool in an application. All tasks that need to be connected must obtain connections from this connection pool.
If you do not use a singleton, multiple connection pools will appear in the application, which makes no sense. If you use Spring and integrate such as druid or c3p0, these mature open source database connection pools are generally implemented in singleton mode by default.
Implementation method of singleton mode
If you search for the implementation of singleton mode in books or websites, you will generally introduce the methods in 5 and 6. Some of them have become less practical with the increase in Java version and the use of multi-threading technology. Here we introduce two methods that are efficient and thread-safe.
1. Static internal class method
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return ; } }
This writing method still uses the JVM itself mechanism to ensure thread safety issues. Since SingletonHolder is private, there is no way to access it except the getInstance() method, it is lazy. At the same time, it will not be synchronized when reading the instance, and there are no performance defects; it does not depend on the JDK version. The above two examples are implemented in this way.
2. Enumeration method
public enum SingleEnum { INSTANCE; SingleEnum(){ ("Constructor Execution"); } public String getName(){ return "singleEnum"; } public static void main(String[] args){ SingleEnum singleEnum = ; (()); } }
We can access the instance through . Moreover, creating enumerations is thread-safe by default, and can also prevent deserialization from causing recreation of new objects.
Static blocks
What is a static block
1. It is executed as the class is loaded, executed only once, and takes precedence over the main function. Specifically, static code blocks are called by classes. When a class is called, the static code block is executed first, and then the main function is executed;
2. Static code blocks are actually initialized for the class, while constructed code blocks are initialized for the object;
3. Variables in static code blocks are local variables, and they are no different from the properties of local variables in ordinary functions;
4. There can be multiple static code blocks in a class;
His writing is like this:
static { ("static executed"); }
Let’s take a look at the following complete example:
public class SingleStatic { static { ("static block is being executed..."); } { ("Constructing code block is in execution..."); } public SingleStatic(){ ("Constructor Execution"); } public static void main(String[] args){ ("main function is executing"); SingleStatic singleStatic = new SingleStatic(); } }
His execution result is as follows:
The static block is being executed...
main function is executing
Constructing code blocks are being executed...
Constructor Execution
From this we can see that their execution order is:
1. Static code blocks
2. Main function
3. Construct code blocks
4. Constructor
This feature can be used to implement singleton mode using static code blocks only when the class is loaded and only once. However, it is not lazy to load, which means that instantiation will be triggered every time the class is loaded.
In addition, regardless of the singleton situation, using the feature of static code blocks, some other functions can be implemented, such as the configuration file loading function mentioned above, which can read the configuration file contents when the class is loaded, which is equivalent to a preloaded function and can be used directly when used.
The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.