Menu
CoddyTech

Пул на основе очереди

Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 47 из 64.

Пулу нужно место для хранения запасных объектов, и в C# для этого есть коллекция Queue<T>: Enqueue добавляет элемент в конец, Dequeue забирает элемент из начала, а Count показывает, сколько элементов ждут. В этом аквариуме пузырьки поднимаются каждые 0.3 секунды, и каждый пузырёк возвращается в пул у поверхности:

using System.Collections.Generic;
using UnityEngine;

public class BubblePool : MonoBehaviour
{
    public GameObject bubblePrefab;
    private Queue<GameObject> pool = new Queue<GameObject>();
    private float timer;

    void Update()
    {
        timer += Time.deltaTime;
        if (timer >= 0.3f)
        {
            timer = 0f;
            Get(new Vector3(Random.Range(-2f, 2f), 0f, 0f));
        }
    }

    public GameObject Get(Vector3 position)
    {
        GameObject bubble;
        if (pool.Count > 0)
        {
            bubble = pool.Dequeue();
        }
        else
        {
            bubble = Instantiate(bubblePrefab);
            bubble.GetComponent<Bubble>().pool = this;
        }
        bubble.transform.position = position;
        bubble.SetActive(true);
        return bubble;
    }

    public void Return(GameObject bubble)
    {
        bubble.SetActive(false);
        pool.Enqueue(bubble);
    }
}
// Bubble.cs, на префабе пузырька
public BubblePool pool;

void Update()
{
    transform.Translate(0f, 2f * Time.deltaTime, 0f);
    if (transform.position.y > 5f) pool.Return(gameObject);
}

Сначала Get проверяет Count, потому что вызов Dequeue для пустой очереди вызывает исключение InvalidOperationException. Только после этого выполняется Instantiate, и устанавливать поле pool нужно только новому пузырьку: у повторно используемого оно уже задано. Очередь выдаёт первым пузырёк, который дольше всех ждал: первым пришёл — первым вышел. Пузырёк у поверхности вызывает Return вместо Destroy и остаётся в сцене неактивным до следующего вызова Get.

Get размещает пузырёк до вызова SetActive(true), потому что его включение сразу запускает OnEnable, и всё, что выполняется там, должно видеть новую позицию, а не ту, где пузырёк исчез в прошлый раз. Именно здесь повторно используемый объект начинает работу заново: Awake и Start выполняются один раз, когда пузырёк создаётся, а OnEnable запускается при каждом вызове Get.

challenge icon

Задание

Средне

Используй пул следов вместо их уничтожения.

  1. В Trail храни запасные объекты в Queue<GameObject>. Return(footprint) деактивирует объект и добавляет его в очередь; Start создаёт prewarm следов и возвращает их.
  2. Get(position) извлекает запасной объект из очереди (или создаёт новый), размещает его и активирует.
  3. В Footprint замени Destroy: сбрасывай возраст в OnEnable; по истечении life секунд вызывай trail.Return(gameObject).

Нажми «Играть» и пройдись: следы исчезнут через 1.5 секунды. Затем нажми «Проверить».

Попробуйте сами

using System.Collections.Generic;
using UnityEngine;

public class Trail : MonoBehaviour
{
    public GameObject footprintPrefab;
    public float step = 1f;
    public int prewarm = 6;
    private Vector3 lastDrop;

    void Start()
    {
        lastDrop = transform.position;
        // Заполните пул заранее созданными отпечатками следов
    }

    void Update()
    {
        if (Vector3.Distance(transform.position, lastDrop) >= step)
        {
            lastDrop = transform.position;
            Get(new Vector3(transform.position.x, 0.02f, transform.position.z));
        }
    }

    public GameObject Get(Vector3 position)
    {
        GameObject footprint = Instantiate(footprintPrefab, position, Quaternion.identity);
        footprint.GetComponent<Footprint>().trail = this;
        return footprint;
    }

    public void Return(GameObject footprint)
    {
        // Запасные отпечатки следов ожидают в пуле
    }
}

Все уроки раздела Архитектура игр в Unity

Потренируйтесь самостоятельно: Песочница Unity