Menu
CoddyTech

الأحداث في C# ضمن Unity

جزء من قسم هندسة الألعاب في Unity في رحلة Unity على Coddy. الدرس 12 من 64.

عندما تتغير النتيجة، تهتم عدة أشياء بذلك: نص النتيجة، وصوت، وإنجاز. إذا استدعى ScoreKeeper كلًا منها، فسيحتاج إلى حقل لكل واحد، وكل ميزة جديدة تعني تعديله. يعكس الحدث ذلك: يعلن ScoreKeeper عن التغيير، ويشترك كل من يهتم به.

تجعل كلمتان في C# ذلك ممكنًا. Action<int> هو نوع طريقة تأخذ int واحدًا ولا تُرجع شيئًا. إن event من هذا النوع هو قائمة بهذه الطرق: يضيف الكود الآخر طريقته باستخدام +=، ويستدعيها المالك كلها باستخدام Invoke. بصيغة C# المبسطة:

using System;

class Alarm
{
    public event Action<int> Rang;

    public void Ring(int hour)
    {
        Rang?.Invoke(hour);
    }
}

class Sleeper
{
    public void WakeUp(int hour)
    {
        Console.WriteLine("Up at " + hour);
    }
}

// في موضع آخر
Alarm alarm = new Alarm();
Sleeper sleeper = new Sleeper();
alarm.Rang += sleeper.WakeUp;
alarm.Ring(7);   // استيقظت في الساعة 7

في Unity، يكون ناشر الحدث والمستمعون إليه مكوّنات، وتوجد عادةً على GameObjects مختلفة:

using System;
using UnityEngine;

public class ScoreKeeper : MonoBehaviour
{
    public event Action<int> OnScoreChanged;
    private int score;

    public void AddPoints(int points)
    {
        score += points;
        OnScoreChanged?.Invoke(score);
    }
}
// ScoreLabel.cs، على نص واجهة عرض المعلومات (HUD)
public ScoreKeeper keeper;
public TMP_Text label;

void Start()
{
    keeper.OnScoreChanged += ShowScore;
}

void ShowScore(int score)
{
    label.text = "Score: " + score;
}

تعني Action<int> أن كل مستمع هو طريقة تأخذ int واحدًا، وهو هنا النتيجة الجديدة. يستدعي Invoke كل طريقة مشتركة، بالترتيب الذي اشتركت به. عندما لا يوجد مستمع، يكون الحدث null، لذا فإن OnScoreChanged(score) العادية ستؤدي إلى استثناء NullReferenceException، بينما يتخطى ?. الاستدعاء بدلًا من ذلك. تقيّد الكلمة المفتاحية event السكربتات الأخرى باستخدام += و-=: ففي ScoreLabel، لا يُترجم keeper.OnScoreChanged(5) أو keeper.OnScoreChanged = null.

ليس لدى ScoreKeeper حقل للتسمية ولا يعرف بوجودها. يشترك عدّاد الكومبو أو مشغّل الصوت بالطريقة نفسها، ويبقى ScoreKeeper كما هو، كما أن مشهد اختبار بلا HUD يعمل دون أخطاء. يعثر المستمع على الناشر عبر مرجع في Inspector، كما في هذا المثال، أو بأي طريقة تعرفها، مثل GetComponent أو singleton. تهم إزالة مستمع باستخدام -= بعد تدمير الكائنات، وهذا موضوع الدرس التالي.

challenge icon

التحدي

سهل

اجعل المقاتل يعلن عن سلسلة ضرباته.

  1. في Fighter.cs، أضف using System; وpublic event Action<int> OnComboChanged;.
  2. استبدل الأسطر التي تستخدم comboText وflame بـ OnComboChanged?.Invoke(combo);، واحذف الحقلين.
  3. في الدالة Start() في ComboText وComboFlame، اكتب fighter.OnComboChanged += Show; و+= Grow;.

اضغط على «العب» واضغط على مفتاح المسافة عدة مرات: تزداد سلسلة الضربات، ثم تُمسح. ثم اضغط على «تحقق».

جرّب بنفسك

using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;

public class Fighter : MonoBehaviour
{
    public float comboWindow = 1f;
    public TMP_Text comboText;
    public Transform flame;
    private int combo;
    private float sinceHit;

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            combo++;
            sinceHit = 0f;
            comboText.text = "Combo x" + combo;
            flame.localScale = Vector3.one * (0.5f + 0.25f * combo);
        }
        else if (combo > 0)
        {
            sinceHit += Time.deltaTime;
            if (sinceHit > comboWindow)
            {
                combo = 0;
                comboText.text = "";
                flame.localScale = Vector3.one * 0.5f;
            }
        }
    }
}

جميع دروس هندسة الألعاب في Unity

تدرّب بنفسك: Playground لـ Unity