ebSkola

3.3 Signāli un objektu komunikācija

Stundas uzdevums: Apgūt Godot signal sistēmu objektu komunikācijai.

SR 2.4.15. Modulāras programmas SR 2.4.18. OOP

80 min plāns: Teorija un paraugs (~10 min) · 1. uzdevums (~20 min) - reģistrē un raidi signālu · 2. uzdevums (~25 min) - pieslēdzies signālam no cita objekta · 3. uzdevums (~20 min) - salīdzini signālu ar tiešu izsaukumu · Iesniegšana (~5 min).

Pirms sāc: atver savu projektu ar dalībnieku klasēm. Signāls ir kā paziņojums: viens objekts pasaka "tas notika", bet citi paši izlemj, vai klausīties.

Teorija: Godot signāli (Signals)

Godot signāli ir veids, kā objekti var sazināties bez tieša savienojuma. Viens objekts emit (raida) signālu, citi connect (pievienojas), lai uz to reaģētu.

using Godot;
using System;

public partial class Participant : Node
{
    [Signal] public delegate void FinishedEventHandler(string name);
    [Signal] public delegate void PenaltyAppliedEventHandler(int amount);

    private int energy = 100;
    private string participantName = "Anna";

    public void ApplyPenalty(int amount)
    {
        energy -= amount;
        EmitSignal(SignalName.PenaltyApplied, amount);
        if (energy <= 0)
            EmitSignal(SignalName.Finished, participantName);
    }
}

Citur kodā:

hero.Finished += OnHeroFinished;

Kā šo izmantot projektā

Atceries: ar redzamu efektu editorā nepietiek. Paskaidro, kura C# klase glabā stāvokli, kura metode to maina un kā Godot node struktūra izmanto šo kodu.

Pārbaudi: katrai būtiskai turnīra lomai ir C# klase vienā .cs failā, ar konstruktoriem, metodēm, signāliem vai mantošanu.

public class OopCheckpoint
{
    public string Lesson = "3.3 Signāli un objektu komunikācija";
    public bool UsesCSharp = true;
    public bool HasScriptFile = true;
    public bool HasSingleResponsibility = true;
    public bool UsesGodotInspector = true;
}

Praktiskie uzdevumi

1. uzdevums -

Reģistrē un raidi signālu

Beigās tavs objekts pratīs paziņot, ka tas beidzis darbu.

  1. Nomaini Participant tā, lai tā mantotu no Node: public partial class Participant : Node.
  2. Pievieno klasē signālu [Signal] public delegate void FinishedEventHandler(string name);.
  3. Uzraksti metodē ApplyPenalty pārbaudi, vai enerģija nokritusi līdz 0.
  4. Izsauc tajā EmitSignal(SignalName.Finished, Name);.
  5. Būvē projektu un pievieno objektu scēnai.
  6. Pārbaudi Godot editorā cilnē Node -> Signals, vai signāls finished tur ir redzams.
  7. Pieraksti, kas notiek, ja [Signal] rindu izdzēš.

Gatavs, kad: Godot editora Signals sarakstā parādās tavs signāls finished.

2. uzdevums -

Pieslēdzies signālam no cita objekta

Beigās cits objekts uzzinās par notikumu, tieši to nepārbaudot.

  1. Izveido C# klasi TournamentManager, kas manto no Node.
  2. Pievieno tai metodi private void OnParticipantFinished(string name).
  3. Paņem dalībnieku ar Participant participant = GetNode<Participant>("Participant");.
  4. Savieno _Ready() metodē signālu ar participant.Finished += OnParticipantFinished;.
  5. Izdrukā apstrādātājā, kurš dalībnieks beidzis darbu.
  6. Būvē un samazini dalībnieka enerģiju līdz nullei.
  7. Pārbaudi Output panelī, ka paziņojums pienāca.

Gatavs, kad: kad dalībnieka enerģija sasniedz 0, menedžeris izdrukā tā vārdu, lai gan pats to nepārbaudīja.

3. uzdevums -

Salīdzini signālu ar tiešu izsaukumu

Beigās tu zināsi, kāpēc signāli ir ērtāki par tiešu saiti.

  1. Pievieno otru dalībnieku un pieslēdz arī to tam pašam signālam.
  2. Samazini abu enerģiju un pārbaudi, ka pienāk divi paziņojumi.
  3. Iedomājies variantu, kur Participant pats izsauktu menedžeri, un pieraksti, ko tad vajadzētu glabāt katrā dalībniekā.
  4. Pieraksti, kas notiktu ar šo variantu, ja menedžera scēnā nav.
  5. Atvieno vienu dalībnieku ar participant.Finished -= OnParticipantFinished; un pārbaudi vēlreiz.
  6. Pieraksti, cik paziņojumu tagad pienāca.
  7. Pieraksti secinājumu: kurš objekts zina par kuru.

Gatavs, kad: abi dalībnieki raida signālu neatkarīgi, un atvienotais vairs neizraisa paziņojumu.

Papildu uzdevums - Pievieno signālam vairāk datu

Ja pamatdarbs ir gatavs, nodod signālā arī skaitli.

  1. Papildini signāla delegate ar otru parametru, piemēram atlikušo enerģiju.
  2. Nodod to EmitSignal izsaukumā.
  3. Papildini apstrādātāja metodi ar otru parametru.
  4. Izdrukā abus datus.
  5. Pārbaudi, ka kompilators sūdzas, ja parametru skaits nesakrīt.

Gatavs, kad: signāls nodod gan vārdu, gan skaitli, un tie abi parādās izdrukā.

Ko sagatavo

Stundas mērķis: Tu proti C# klasē reģistrēt Godot signālu, to raidīt notikuma brīdī un pieslēgt tam citu objektu, un vari izskaidrot, kāpēc signāls ir ērtāks par tiešu izsaukumu.

GitHub krātuvē jābūt:

  1. Faili ar [Signal], EmitSignal un += signāla pieslēgumu.
  2. Ekrānuzņēmums ar Godot Node -> Signals cilni, kurā redzams tavs signāls.
  3. Ekrānuzņēmums ar Output paneli, kurā redzami abu dalībnieku paziņojumi.
  4. README.md: viens teikums par to, kurš objekts par kuru zina, lietojot signālus.

Kā iesniedz: Veic commit un push. Skolotāja norādītajā vietā ievieto tikai GitHub krātuves saiti; visiem prasītajiem failiem, README.md pierakstiem un pierādījumiem jābūt pašā krātuvē.

Biežākās kļūdas

  • EmitSignal nav efekta: signāls nav pieteikts ar [Signal] vai netiek izsaukts pareizais SignalName.
  • Handler kļūda: apstrādātāja parametru skaitam un tipiem jāsakrīt ar signāla delegate.
  • Signāls saņemts vairākkārt: += izsaukts vairākas reizes - pieslēdz signālu vienā vietā, parasti _Ready().

Godot ekrānuzņēmumi

Diagramma ar Participant objektu kreisajā pusē, signāliem penalty_applied/finished vidū un HUD ar Game klasi labajā pusē - bultiņas norāda signal flow.
Diagramma ar Participant objektu kreisajā pusē, signāliem penalty_applied/finished vidū un HUD ar Game klasi labajā pusē - bultiņas norāda signal flow.

Koda piemērs (paplašināts)

using Godot;
using System;

public partial class Participant : Node
{
    [Signal] public delegate void PenaltyAppliedEventHandler(int amount);
    [Signal] public delegate void FinishedEventHandler(string name);

    private int energy = 100;
    private string participantName = "Anna";

    public void ApplyPenalty(int amount)
    {
        energy = Math.Max(0, energy - amount);
        EmitSignal(SignalName.PenaltyApplied, amount);
        if (energy == 0)
            EmitSignal(SignalName.Finished, participantName);
    }
}

public partial class Game : Node
{
    public override void _Ready()
    {
        Participant anna = GetNode<Participant>("Anna");
        anna.PenaltyApplied += OnPenalty;
        anna.Finished += OnFinished;
    }

    private void OnPenalty(int amount) { }
    private void OnFinished(string name) { }
}
HUD atjauninās automātiski, kad dalībniekam mainās enerģija vai viņš pabeidz raundu, bez tiešas references.