ObjectPool في Unity
جزء من قسم هندسة الألعاب في Unity في رحلة Unity على Coddy. الدرس 39 من 50.
توفّر Unity مجموعة تجميع، وهي ObjectPool<T> ضمن UnityEngine.Pool. تحدد لها ما تفعله في كل خطوة باستخدام أربع دوال لامبدا، وهي تحتفظ بالكائنات الاحتياطية والأعداد. تُصدر هذه المدخنة نفثة دخان كل 0.2 ثانية، وتعود كل نفثة بعد ثانية واحدة:
using UnityEngine;
using UnityEngine.Pool;
public class Chimney : MonoBehaviour
{
public Puff puffPrefab;
private ObjectPool<Puff> pool;
private float timer;
void Awake()
{
pool = new ObjectPool<Puff>(
createFunc: () => Instantiate(puffPrefab),
actionOnGet: puff => puff.gameObject.SetActive(true),
actionOnRelease: puff => puff.gameObject.SetActive(false),
actionOnDestroy: puff => Destroy(puff.gameObject),
collectionCheck: true,
defaultCapacity: 10,
maxSize: 20);
}
void Update()
{
timer += Time.deltaTime;
if (timer >= 0.2f)
{
timer = 0f;
Puff puff = pool.Get();
puff.transform.position = transform.position + Vector3.up;
puff.Launch(pool);
}
}
}// Puff.cs
private IObjectPool<Puff> pool;
private float age;
public void Launch(IObjectPool<Puff> owner)
{
pool = owner;
age = 0f;
}
void Update()
{
transform.Translate(0f, Time.deltaTime, 0f);
age += Time.deltaTime;
if (age >= 1f) pool.Release(this);
}تعمل createFunc عندما يجد Get أن مجموعة التجميع فارغة؛ وتعمل actionOnGet مع كل Get، بينما تعمل actionOnRelease مع كل Release. عندما تُعاد نفثة بينما يوجد بالفعل maxSize من الكائنات الاحتياطية في الانتظار، لا تحتفظ بها مجموعة التجميع: بل تدمرها actionOnDestroy. إن defaultCapacity هو فقط الحجم الابتدائي لقائمة مجموعة التجميع، لذلك لا يُنشأ أي شيء مسبقًا. يحصي CountAll النفثات التي أنشأتها مجموعة التجميع وما زالت تملكها، أما CountActive فيحصي تلك الموجودة في اللعبة، وCountInactive فيحصي الكائنات الاحتياطية. يُفعّل Get النفثة قبل أن تضعها المدخنة، وهذا مناسب هنا لأن Puff لا يحتوي على OnEnable يقرأ موضعه. تحتفظ النفثة بمجموعة التجميع بصفتها IObjectPool<Puff>، وهي الواجهة التي تطبقها كل مجموعة تجميع في Unity، كما تعيد Launch ضبط عمرها عند كل استخدام.
عند تفعيل collectionCheck، تؤدي إعادة نفثة موجودة بالفعل في مجموعة التجميع إلى طرح InvalidOperationException: Trying to release an object that has already been released to the pool. لا يُجرى هذا الفحص إلا في المحرر، لأنه يبحث بين الكائنات الاحتياطية عند كل Release؛ أما في النسخة المبنية فستبقى النفثة في مجموعة التجميع مرتين، وسيعيد استدعاء Get الكائن نفسه مرتين. من الأسباب الشائعة لذلك كائن يواصل العمل بعد إعادته إلى مجموعة التجميع ثم يعيد نفسه إليها مرة أخرى، وهذا هو سبب إيقاف actionOnRelease له.
التحدي
متوسطالألعاب النارية. تؤدي كل ضغطة على مفتاح المسافة إلى إطلاق 8 شرارات من القاذف، وتستدعي كل Spark الدالة Return(this) الخاصة بالقاذف عند انتهاء عمرها البالغ ثانية واحدة (يكون Spark جاهزًا). ينشئ القاذف كل شرارة ويدمرها في كل مرة. استخدم مجمّع Unity:
- في
Launcher.cs، أنشئObjectPool<Spark>في Awake. ينشئ المجمّع شرارة عبر إنشاء نسخة منsparkPrefab، ويفعّل الشرارة عند إخراجها ويعطّلها عند إعادتها، ويدمر كائن الشرارة عند تحريرها. - يحتفظ المجمّع بما يصل إلى 5 شرارات احتياطية، لذا بعد إطلاق دفعة من 8 شرارات، تنتظر 5 شرارات وتُدمّر 3.
- تأخذ ضغطة مفتاح المسافة شراراتها الثماني من المجمّع بدلًا من إنشائها، وتُعيد
Returnالشرارة إلى المجمّع بدلًا من تدميرها.
اضغط Play، وأطلق بضع دفعات، ثم اضغط Check.
جرّب بنفسك
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;
public class Launcher : MonoBehaviour
{
public Spark sparkPrefab;
public int sparks = 8;
void Awake()
{
// أنشئ المجمّع: إنشاء الشرارات والحصول عليها وإعادتها وتدميرها، مع الاحتفاظ بما لا يزيد عن 5 شرارات احتياطية
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
for (int i = 0; i < sparks; i++)
{
Spark spark = Instantiate(sparkPrefab);
spark.transform.position = transform.position + Vector3.up;
spark.Launch(Direction(i), this);
}
}
}
// شرارة انتهى عمرها
public void Return(Spark spark)
{
Destroy(spark.gameObject);
}
// إلى الخارج والأعلى، موزّعة بالتساوي حول القاذف
Vector3 Direction(int i)
{
return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس هندسة الألعاب في Unity
1المكوّنات لا التسلسلات الهرمية
مكوّنات صغيرةسكربت أساسي للعدومكوّنات مجرّدةتعمل المكوّنات معًامراجعة: برج دفاعي معياري4البيانات المتسلسلة
الأصناف القابلة للتسلسلالقوائم في Inspectorالتعدادات في Inspectorمراجعة: جدول الغنائم10مشروع: الناجي في الساحة
البطلأنواع الأعداء