Главная » Статьи » Программирование » Unity

Unity. Система спавна на основе очереди без уничтожения

Вы можете использовать способ, описанный в этой статье в случае, если в вашей игре враги или что вы там спавните не уничтожаются после смерти при помощи GameObject.Destroy(gameObject), а просто выключается gameObject.SetActive(false). Очередь объектов представляет собой список объектов или массив как вам удобнее, который увеличивается или уменьшается в зависимости от текущей ситуации и требований других объектов в игре (например, триггер, который говорит, что тут необходимо заспавнить врага. Так же, если вы уничтожаете объекты после смерти рекомендую это пересмотреть потому, что клонирование объекта из префаба при появлении и потом еще уничтожение занимает время, а включение и выключение объектов занимает меньше времени, хотя, потребуется лишняя оперативная память. 

Сразу могу сказать, что пример, который я буду разбирать может совершенно не подойти к вашей игре потому, что к каждой игре нужен свой подход и универсальных методов типа системы спавна не бывает так как у каждой игры на это свои требования, за исключением однотипных игр. Скрипт состоит из списка типа List<PoolItemScript> и нескольких функций для управления процессами, происходящими в классе.

public class PoolItemScript : MonoBehaviour {

    public bool use = false;
    public bool enab = false;
    float timeLastActivation;
    public bool del = false;

    void OnEnable() {
        timeLastActivation = UnityEngine.Time.time;
        enab = true;
    }

    void OnDisable () {
        enab = false;
    }

    public float GetLastTimeActivation() {
        return timeLastActivation;
    }

    public bool IsFree() {
        return use == false && enab == false && del == false;
    }

    public void SetEnab_and_NoUse() {
        gameObject.SetActive (true);
        use = false;
    }
}

Данный скрипт устанавливается на префаб, который будет в очереди. Скрипт хранит информацию о том включен в данный момент объект или нет. Кто-то спросит зачем мне это, но ответ прост, я в своих играх использую реактивные расширения и так как там не всегда операции идут в базовом потоке, то лучше если информация о рабочем стоянии объекта будет в переменной, которую можно считывать из любого потока. Информацию про использование в данный момент объекта (переменная use), имеется ввиду использование объекта очередью, изменение характеристик или спавн в новом месте. Переменная del, означает будет удален объект в ближайшее время или нет. Объекты удаляются в случае, если оно давно не использовались, поэтому тут мы видим переменную обозначающую время последнего использования.

public class PoolScript : MonoBehaviour {

    public float intervalProvList = 60;
    public PoolItemScript prefab;
    List<PoolItemScript> pool = new List<PoolItemScript>();
    float time1;

    void Start () {
        time1 = UnityEngine.Time.time;
    }

    IEnumerator WaitAndDel(List<PoolItemScript> items) {
        float time2 = intervalProvList * 10;
        int n = items.Count ();
        for (int i = 0; i < n; i++) {
            float time = UnityEngine.Time.time;
            while ((pool [i].use == true || pool [i].enab == true) && UnityEngine.Time.time - time < time2)
                yield return null;
            pool.Remove (items [i]);
            Destroy (items [i].gameObject);
        }
    }

    List<PoolItemScript> GetDelList(float time) {
        List<PoolItemScript> list = new List<PoolItemScript> ();
        int n = pool.Count ();
        for (int i = 0; i < n; i++) {
            PoolItemScript item = pool [i];
            if (time - item.GetLastTimeActivation () > intervalProvList) {
                item.del = true;
                list.Add (item);
            } else if (item.del == true)
                list.Add (item);
        }
        return list;
    }

    void Update() {
        if (UnityEngine.Time.time - time1 > intervalProvList) {
            StopAllCoroutines ();
            List<PoolItemScript> list = GetDelList(UnityEngine.Time.time);
            StartCoroutine (WaitAndDel (list));
            time1 = UnityEngine.Time.time;
        }
    }

    public int CountEnable() {
        return pool.Count (x => x.enab && x.del == false);
    }

    public int Count() {
        return pool.Count (x => x.del == false);
    }

    public int CountDisable() {
        return pool.Count (x => x.enab == false && x.del == false);
    }

    public PoolItemScript GetFreeItem() {
        PoolItemScript item = pool.FirstOrDefault (x => x.IsFree ());
        if (item == null) {
            item = GameObject.Instantiate (prefab);
            item.gameObject.SetActive (false);
            item.use = true;
            item.transform.parent = transform;
            pool.Add (item);
        } else
            item.use = true;
        return item;
    }
}

В этом скрипте работает очередь только для одного объекта. В глобальных переменных есть переменная обозначающая количество секунд в интервалах между проверками очереди, префаб, список скриптов объектов очереди. В функции Update в определенный интервал времени запускается механизм удаления из очереди объектов, которые давно не использовались, например, если у нас игра жанра РПГ, то в пределах мирных зон вполне можно потихоньку отчистить очередь спавна. Так отчистка в данной версии скрипта может спасти игру от ситуации, когда объект где-то затерялся и никак не выключается ну мало ли баг появился и объект не умер или провалился в текстуры; бывают разные ситуации. GetFreeItem – именно это метод будет выдавать скрипту спавна свободные объекты, если таковых нет он их создаст из добавит в очередь. Регулировка количества объектов в очереди у меня возложена скрипт спавна, но в вашем случае она может размещена в скрипте очереди.

public class SpawnScript : MonoBehaviour {

    public PoolScript poolScript;
    public float intervalSpawn = 3;
    public int maxSpawn;
    float time1, xStatic, yMin, yMax;

    void Start () {
        time1 = UnityEngine.Time.time;
        float dist = (transform.position - Camera.main.transform.position).z;
        xStatic = Camera.main.ViewportToWorldPoint (new Vector3 (1, 0, dist)).x + 3;
        yMin = Camera.main.ViewportToWorldPoint (new Vector3 (0, 0, dist)).y + 1;
        yMax = Camera.main.ViewportToWorldPoint (new Vector3 (0, 1, dist)).y - 1;
    }
        
    void Update () {
        if (UnityEngine.Time.time - time1 > intervalSpawn) {
            if (poolScript.Count () <= maxSpawn) {
                PoolItemScript item = poolScript.GetFreeItem ();
                float yPos = UnityEngine.Random.Range (yMin, yMax);
                item.transform.position = new Vector3 (xStatic, yPos, item.transform.position.z);
                item.SetEnab_and_NoUse ();
            }
            time1 = UnityEngine.Time.time;
        }
    }
}

Скрипт скрипта в этом примере очень простой. Несколько переменных: интервал между спавнами, максимальное количество врагов, позиции спавна врагов. В Start мы рассчитываем позиции, в пределах которых будут появляться враги. Когда в функции Update приходит время для появления нового врага, мы запрашиваем у скрипта очереди необходимый объект, далее устанавливает необходимые характеристики объекта, в моем случае это только позиция объекта, у вас могут быть и другие характеристики. Далее мы включаем объект используя метод в скрипте PoolItemScript.

Я сделал небольшой пример. Скачать его можно по ссылке - http://cod2014.ru/publ_files/unity/ObjectsList.zip

Удачных разработок.

Категория: Unity | Добавил: Алексей (03.04.2017) | Автор: Фролов Алексей Алексеевич E
Просмотров: 2077 | Рейтинг: 5.0/1
Всего комментариев: 0
avatar