ایجاد و حذف مکرر GameObjectها یکی از دلایل افت عملکرد در بازیهای اکشن و موبایل است. گلوله، دشمن، افکت برخورد، متن آسیب و ذرات معمولاً عمر کوتاهی دارند و بارها ساخته میشوند. Instantiate و Destroy مداوم باعث تخصیص حافظه و فعالیت Garbage Collector میشود.
Object Pooling اشیا را از قبل میسازد و پس از استفاده غیرفعال میکند تا دوباره مورد استفاده قرار گیرند. در این آموزش یک Pool عمومی و قابل توسعه میسازیم.
ساخت Object Pooling در Unity
Object Pool چگونه کار میکند؟
در شروع بازی تعدادی نمونه از Prefab ساخته و داخل یک مجموعه نگهداری میشوند. هنگام نیاز، یک نمونه غیرفعال دریافت و فعال میشود. پس از پایان عمر، شیء بهجای Destroy شدن به Pool بازمیگردد.
قرارداد اشیای قابل Pool
شیء هنگام دریافت باید وضعیت خود را Reset کند و هنگام بازگشت رویدادها، Coroutineها و افکتهای فعال را متوقف کند. برای استانداردسازی میتوان یک Interface تعریف کرد.
public interface IPoolable
{
void OnSpawned();
void OnDespawned();
}ساخت Pool پایه
using System.Collections.Generic;
using UnityEngine;
public class GameObjectPool
{
private readonly Queue items = new();
private readonly GameObject prefab;
private readonly Transform root;
public GameObjectPool(GameObject prefab, int initialSize, Transform root)
{
this.prefab = prefab;
this.root = root;
for (int i = 0; i < initialSize; i++)
CreateItem();
}
private GameObject CreateItem()
{
GameObject item = Object.Instantiate(prefab, root);
item.SetActive(false);
items.Enqueue(item);
return item;
}
public GameObject Get(Vector3 position, Quaternion rotation)
{
if (items.Count == 0)
CreateItem();
GameObject item = items.Dequeue();
item.transform.SetPositionAndRotation(position, rotation);
item.SetActive(true);
item.GetComponent()?.OnSpawned();
return item;
}
public void Release(GameObject item)
{
item.GetComponent()?.OnDespawned();
item.SetActive(false);
item.transform.SetParent(root);
items.Enqueue(item);
}
} مدیریت چند Prefab
پروژه واقعی چند نوع گلوله، دشمن و افکت دارد. Pool Manager میتواند Dictionary از شناسه Prefab به Pool نگهداری کند. بهتر است از رشته برای جستوجوی پرتکرار استفاده نشود و مرجع یا شناسه ثابت تعریف شود.
تعیین اندازه اولیه
Pool بسیار کوچک هنگام بازی مجبور به ساخت نمونه جدید میشود و Pool بسیار بزرگ حافظه را هدر میدهد. بیشترین تعداد همزمان مورد انتظار را اندازهگیری کنید. برای مثال نرخ شلیک، عمر گلوله و تعداد سلاحها تخمینی از تعداد گلوله فعال میدهد.
سیاست رشد و محدودیت
مشخص کنید اگر Pool خالی شد چه اتفاقی بیفتد: ساخت نمونه جدید، استفاده مجدد از قدیمیترین نمونه یا رد درخواست. برای افکت تزئینی میتوان درخواست اضافی را رد کرد، اما برای گلوله اصلی بازی این کار ممکن است گیمپلی را تغییر دهد.
بازنشانی وضعیت
فقط تغییر موقعیت کافی نیست. Rigidbody باید سرعت و چرخش قبلی را پاک کند، TrailRenderer باید Clear شود، Animator به حالت اولیه برگردد و متغیرهایی مانند سلامتی دوباره مقداردهی شوند. رویدادهای قبلی نیز باید لغو شوند.
بازگشت خودکار
گلوله میتواند پس از برخورد یا پایان زمان به Pool بازگردد. Coroutine قدیمی باید هنگام Despawn متوقف شود تا پس از Spawn بعدی شیء را زودتر غیرفعال نکند. استفاده از Cancellation Token یا مدیریت دقیق Coroutine مفید است.
استفاده برای رابط کاربری
Pooling فقط برای اشیای صحنه نیست. پیامها، آیتمهای فروشگاه، ردیفهای Leaderboard و Damage Text نیز میتوانند بازیافت شوند. در فهرستهای بزرگ، Virtualization نتیجه بهتری از ساخت تمام عناصر دارد.
ObjectPool داخلی Unity
Unity در نسخههای جدید کلاس UnityEngine.Pool.ObjectPool را ارائه میدهد. این ابزار Callbackهای ایجاد، دریافت، آزادسازی و نابودی دارد و نیاز به نوشتن Queue اختصاصی را کاهش میدهد. انتخاب میان نسخه داخلی و سفارشی به نیاز پروژه بستگی دارد.
اشتباهات رایج
- فراموش کردن Reset وضعیت Rigidbody و Trail
- ثبت چندباره Event Listener
- برگشت دادن یک شیء به Pool بیش از یک بار
- استفاده از یک Pool برای Prefabهای ناسازگار
- ساخت تعداد بسیار زیاد نمونه در شروع Scene
- فرض اینکه Pooling همیشه عملکرد را بهتر میکند
اندازهگیری با Profiler
پیش و پس از Pooling، CPU Usage، GC Alloc و Memory را مقایسه کنید. اگر یک شیء بهندرت ساخته میشود، افزودن Pool پیچیدگی غیرضروری ایجاد میکند. Pooling بیشترین ارزش را برای اشیای پرتکرار و کوتاهعمر دارد.
کاربرد Object Pooling در پروژه حرفهای
نسخه ساده Pool در یک روز قابل ساخت است، اما پشتیبانی از Addressables، بارگذاری Async، محدودیت حافظه و Pool شبکهای طراحی بیشتری نیاز دارد. در بازی آنلاین، Spawn منطقی سرور با نمایش محلی اشیا هماهنگ میشود، اما مالکیت و وضعیت شبکه باید جدا باقی بماند.
JPGames سیستم Object Pooling را همراه با Profiler و بهینهسازی واقعی پروژه پیادهسازی میکند. اگر بازی Unity شما هنگام شلیک، Spawn دشمن یا نمایش افکت افت فریم دارد، پروژه را برای بررسی فنی و دریافت برآورد ارسال کنید.