3D Game Pr ramming Design (5): Interact with the game world (object pool)

Implemented the core framework, no interface

PS: Reminds me that I used XNA to write a rudimentary version of the beach landing for half a summer vacation. The old hard work is ugly, and the current visualization engine is much more powerful than the previous one.

The framework is an extension of the old framework written before, just look at the UML diagram.


TotalManager, LevelManager, UIManager I have done it once in the previous article. The part responsible for VC in the standard MVC, M is the following Level_N.

Unity has already implemented the component mode for us, so we only need to build an empty object, then hang the three scripts of EnemyCreate, Shoot, and UIController, and then make a Prefab, which can be called in the LevelManager.

Because of these tool classes, I hope they will only generate one, so they are all made into singleton mode (it is a sin to type one more line of repetitive code). In detail, it inherits a UnitySingleton generic, which is implemented as follows:

UnitySingleton.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UnitySingleton<T> : MonoBehaviour
    where T : Component
    //Constraint T needs to inherit Component
{
    //protected ensures that inherited subclasses can access
    //But it also leads to other subclasses that also inherit UnitySingleton can also access, but the problem is not big
    protected static T _instance;

    public static T Instance() {
        if (_instance == null) {
            //find the instance we have instantiated in the scene
            _instance = FindObjectOfType(typeof(T)) as T;
            //If there is no instance in our scene, create an empty object and add the script to return this instance
            if (_instance == null) {
                GameObject obj = new GameObject();
                obj.name = typeof(T).ToString();
                _instance = obj.AddComponent<T>();
            }
        }
        return _instance;
    }
}

Inherit directly. Note that I did not declare the constructor in the generic type, I need to privatize it in the subclass to prevent it from being instantiated by external new

public class UIController : UnitySingleton<UIController>{
    private UIController() { }
}

There is nothing to say about the logical processing of Shoot. It is a raycast solution. Of course, there are many things that can be played here. For example, instead of ray detection, it is made into FreeFPS to use bullets to hit flying saucers (let’s put it bluntly, or land on the beach)

Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit; //Store the information of the collided object
bool isCollider = Physics.Raycast(ray,out hit); //The detected object must have a collider to be detected
if (isCollider) {
	hit.transform
	hit.collider
	hit.point
}

The direction variable is stored in ray, and we can easily get the shooting angle of the bullet.

But whether bullets or flying saucers are objects that are created frequently, if they are continuously instantiated + destroyed, one will fragment the memory (of course, UNITY will help you deal with this content), and the other is for performance. The loss is extremely high, because the complexity of creating an instance is O(n)

At this time, we can use a class called "object pool" to solve this problem (a derivation of the factory pattern). We instantiate a certain number of objects in advance, take them out when they are used, and stuff them back into the object pool when they are used up, avoiding the operations of instantiation and release. At this time, the complexity is O(1).

The method of storing objects in the object pool can be selected arbitrarily. There is no problem with List, Stack, Dictionary, and array. Choose the appropriate storage method according to your own needs.

I give an example of using Stack below

public class ObjectPool<T> where T : class, new()
{
    private Stack<T> m_objectStack = new Stack<T>();

    public T Get()
    {
        return (m_objectStack.Count == 0) ? new T() : m_objectStack.Pop();
    }

    public void Return(T t)
    {
        m_objectStack.Push(t);
    }
}

Very simple code, call the Get method when you need an object, take an instance from the Stack to use, and instantiate one on the spot if the object in the Stack has been used up.

When the object is used up, return it, press it back into the stack and wait for the next use

This code is not instantiated in advance, you need to instantiate it in advance and fill in an Init method, and return a certain number of object instances.

There are two advantages of using Stack, 1. Simple, using the standard library, you can write such an object pool in two minutes, 2. The size of the object pool is expandable, and no error will be reported due to out-of-bounds. The disadvantage is also because of scalability, which leads to the inability to deal with the problem of memory fragmentation (of course, you don't need to consider this when using UNITY)

Note that using the object pool in multi-threading requires adding locks, otherwise thread safety cannot be guaranteed (Unity still does not need to worry about this problem)

Finally, Unity has a script plug-in called PoolManager, which is very powerful. In practical applications, this plug-in can meet 99% of the needs.


github link: https://github.com/keven2148/3D-Game-Programming-Design-Lesson-Work/tree/master/Lesson5

Version: Unity2017.3

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324442071&siteId=291194637