الموجة التالية
الدرس 17 من 18 في دورة لعبة Unity: الدفاع عن الأبراج على Coddy.
بين الموجات، يبني اللاعب، ويعرض العدّ التنازلي الوقت المتبقي. يمكن للاعب المستعد استدعاء الموجة التالية مبكرًا باستخدام زر. الاستراحة عبارة عن حلقة في coroutine تُنتج null، لذا تعمل مرة واحدة في كل إطار: تحسب الوقت المتبقي تنازليًا، وتعرضه، وتنتهي عندما ينفد الوقت أو عندما يطلب الزر بدء الموجة. يعيّن الزر قيمة bool فقط، وتقرأها الحلقة:
while (left > 0f && !skipRequested)
{
// أظهر الثواني المتبقية، وابدأ العد التنازلي، وانتظر إطارًا واحدًا
}
skipRequested = false;تُعاد قيمة bool إلى false بعد استخدامها، وإلا فستنتهي كل استراحة بعد الأولى فورًا. تقوم Mathf.CeilToInt بالتقريب إلى الأعلى، لذا فإن قراءة الوقت المتبقي البالغ 2.3 ثانية تكون 3، ويعرض العدّ التنازلي الرقم 1 طوال الثانية الأخيرة بدلًا من 0. العدّ التنازلي عبارة عن coroutine مستقلة، ويشغّل yield return StartCoroutine(Countdown()); هذه coroutine وينتظر انتهاءها قبل أن تتابع حلقة الموجات.
تعرض واجهة HUD أيضًا رقم الموجة. يعرفه المُولِّد، ويعرضه GameManager، لذا يطلق المُولِّد حدث C#، وهو WaveStarted، مع الموجة وعدد الموجات. وعلى عكس الأحداث الثابتة الخاصة بالأعداء، يرتبط هذا الحدث بمُولِّد واحد، ويشترك GameManager فيه عبر الحقل spawner.
التحدي
صعب- في
WaveSpawner، أضِف الحقلين المُسلسلينcountdownText(من النوع TMP_Text) وnextWaveButton(من النوع GameObject)، وأضِفpublic void CallNextWave()التي يستدعيها الزر لطلب بدء الموجة الآن. - استبدل الانتظار قبل كل موجة بعدٍّ تنازلي يُشغَّل باستخدام
yield return StartCoroutine(Countdown());. أثناء تشغيله، يظهر الزر ويعرض النصNext wave in 8، مع تقريب الثواني المتبقية إلى الأعلى. ينتهي عند نفاد الوقت أو النقر على الزر؛ بعد ذلك يصبح النص فارغًا ويُخفى الزر. - أضِف
public event Action<int, int> WaveStarted، واستدعِ الحدث عند بدء الموجة مع تمرير رقمها (بدءًا من 1) وعدد الموجات. - في
GameManager، أضِف الحقلين المُسلسلينspawner(من النوع WaveSpawner) وwaveText(من النوع TMP_Text)، واشترك في الحدث WaveStarted، واعرضWave 1/8.
تحتوي واجهة HUD على WaveText وCountdownText وNextWaveButton. اضغط Play، وأنشئ برجًا، ثم ابدأ الموجة الأولى مبكرًا، وبعدها اضغط Check.
جرّب بنفسك
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WaveSpawner : MonoBehaviour
{
[SerializeField] private List<Wave> waves = new List<Wave>();
[SerializeField] private Transform[] waypoints;
[SerializeField] private float timeBetweenWaves = 5f;
void Start()
{
StartCoroutine(RunWaves());
}
IEnumerator RunWaves()
{
for (int i = 0; i < waves.Count; i++)
{
yield return new WaitForSeconds(timeBetweenWaves);
Debug.Log("Wave " + (i + 1));
Wave wave = waves[i];
for (int n = 0; n < wave.count; n++)
{
Enemy enemy = Instantiate(wave.enemy, waypoints[0].position, Quaternion.identity);
enemy.waypoints = waypoints;
yield return new WaitForSeconds(wave.spacing);
}
}
Debug.Log("All waves sent");
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.