ebSkola

6.4 Atkļūdošana un profilēšana

Stundas uzdevums: Apgūt Godot debugger, breakpoints, un izmantot to, lai atrastu bugs.

SR 2.4.10. Atkļūdošana SR 2.4.19. Algoritmi SR 2.4.5. Datu analīze un vizualizācija

80 min plāns: Teorija un paraugs (~10 min) · 1. uzdevums (~20 min) - atrodi kļūdu ar izdrukām · 2. uzdevums (~25 min) - lieto breakpoint un mainīgo skatu · 3. uzdevums (~20 min) - atrodi lēnāko vietu ar profileri · Iesniegšana (~5 min).

Pirms sāc: atver savu spēli. Šodien apzināti salauzīsi kodu un tad mācīsies to atrast - tā ir prasme, ko vajadzēs katrā projektā.

Teorija: Debugger un profileris

Godot piedāvā vairākus rīkus problēmu atrašanai:

  • Output panel - GD.Print() un GD.PrintErr() ziņojumi.
  • Debugger - breakpoints, step through, watch variables.
  • Profiler - funkciju laika analīze.
  • Network Profiler - RPC un networking.
  • Visual Profiler - vizuāls diagrammu uzziņa.
// Debugging C# kodā
GD.Print("Player position: ", Position);
GD.PrintErr("Error: enemy is null!");
GD.PushWarning("Suspicious value: " + value);

// Pašpārbaudes
if (player == null)
{
    GD.PrintErr("Player not found!");
    return;
}
if (hp < 0)
{
    throw new System.InvalidOperationException("Negative HP!");
}

// Nosacīta debug izvade
#if DEBUG
GD.Print("Function called: ", nameof(TakeDamage));
#endif

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: UI signāli, audio darbības, debug izvade un publicēšanas sagatave ir sasaistīta ar C# spēles stāvokli.

public class PolishCheckpoint
{
    public string Lesson = "6.4 Atkļūdošana un profilēšana";
    public bool UsesCSharp = true;
    public bool ConnectsUiSignals = true;
    public bool RoutesAudioEvents = true;
    public bool VerifiesExportBuild = true;
}

Praktiskie uzdevumi

1. uzdevums -

Atrodi kļūdu ar izdrukām

Beigās tu pratīsi atrast, kurā vietā vērtība kļūst nepareiza.

  1. Ievies savā kodā apzinātu kļūdu, piemēram nomaini + uz -.
  2. Palaid spēli un pieraksti, kas izskatās nepareizi.
  3. Pievieno GD.Print("pozicija: ", Position); pirms aprēķina.
  4. Pievieno tādu pašu izdruku pēc aprēķina.
  5. Palaid un salīdzini abas vērtības.
  6. Atrodi pēc izdrukām precīzo rindu, kur vērtība kļūst nepareiza.
  7. Salabo kļūdu un izdzēs izdrukas.

Gatavs, kad: tu vari nosaukt konkrētu rindu, kurā vērtība kļuva nepareiza, un kļūda ir izlabota.

2. uzdevums -

Lieto breakpoint un mainīgo skatu

Beigās tu pratīsi apturēt spēli un apskatīt visus mainīgos uzreiz.

  1. Atver cilni Debugger.
  2. Uzliec breakpoint uz vienas koda rindas, uzklikšķinot rindas numura kreisajā malā.
  3. Palaid spēli un pagaidi, līdz tā apstājas.
  4. Apskati panelī Stack Variables visu mainīgo vērtības.
  5. Nospied Step Into un vēro, kā izpilde iet pa vienai rindai.
  6. Pieraksti, kāda vērtība bija tavam galvenajam mainīgajam.
  7. Pieraksti, kad breakpoint ir ērtāks par izdrukām.

Gatavs, kad: spēle apstājas norādītajā rindā, un tu vari nolasīt mainīgo vērtības, tās neizdrukājot.

3. uzdevums -

Atrodi lēnāko vietu ar profileri

Beigās tu zināsi, kura funkcija patērē visvairāk laika.

  1. Atver Debugger -> Profiler un nospied Start.
  2. Spēlē 30 sekundes ar aktīvu spēli.
  3. Aptur profileri un sakārto sarakstu pēc laika.
  4. Pieraksti trīs funkcijas, kas aizņem visvairāk laika.
  5. Pārbaudi, vai kāda no tām tiek izsaukta biežāk, nekā vajadzētu.
  6. Salīdzini _Process un _PhysicsProcess kopējo laiku.
  7. Pieraksti vienu ideju, ko varētu uzlabot.

Gatavs, kad: tev ir pierakstītas trīs laikietilpīgākās funkcijas un viena konkrēta uzlabojuma ideja.

Papildu uzdevums - Pievieno pašpārbaudes izdrukas

Ja pamatdarbs ir gatavs, liec kodam pašam brīdināt par kļūdām.

  1. Pievieno pārbaudi, vai svarīgs mezgls tiešām tika atrasts.
  2. Izdrukā GD.PrintErr("Nav atrasts mezgls!");, ja tas ir null.
  3. Pārsauc mezglu scēnā un palaid spēli.
  4. Pieraksti, ar ko sarkanā GD.PrintErr izdruka atšķiras no parastās.
  5. Atgriez nosaukumu un pārbaudi, ka brīdinājums pazūd.

Gatavs, kad: pārsaucot mezglu, konsolē parādās sarkans brīdinājums, nevis spēle klusi avarē.

Ko sagatavo

Stundas mērķis: Tu proti atrast kļūdu trīs veidos: ar izdrukām, ar breakpoint un mainīgo skatu, un ar profileri atrast laikietilpīgāko funkciju.

GitHub krātuvē jābūt:

  1. Ekrānuzņēmums ar Debugger paneli, kurā spēle apturēta breakpoint vietā un redzami mainīgie.
  2. Ekrānuzņēmums ar profilera sarakstu, sakārtotu pēc laika.
  3. README.md: kura rinda saturēja apzināti ievesto kļūdu un kā tu to atradi.
  4. README.md: trīs laikietilpīgākās funkcijas un viena uzlabojuma ideja.

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

  • Breakpoint nestrādā: Palaid projektu no Godot .NET debug režīmā un pārliecinies, ka C# projekts ir uzbūvēts bez kļūdām.
  • Print panel pārplūst: Pārāk daudz print - lieto conditional vai noņem.
  • Profiler crashes: Pārliecinies par jaunāko Godot versiju.

Godot ekrānuzņēmumi

Godot Debugger ar breakpoint kodā, Stack panelī call stack, Locals panelī active variables ar to vērtībām.
Godot Debugger ar breakpoint kodā, Stack panelī call stack, Locals panelī active variables ar to vērtībām.
Profiler logs ar function timing tabulu - top funkcijas pēc 'Total Time' sortētas, ar grafu vēsturei.
Profiler logs ar function timing tabulu - top funkcijas pēc 'Total Time' sortētas, ar grafu vēsturei.

Koda piemērs (paplašināts)

using Godot;
using System;

public partial class Player : CharacterBody2D
{
    [Signal] public delegate void DamagedEventHandler(int amount);

    private string playerName = "Player";
    private int hp = 100;

    public void TakeDamage(int amount)
    {
        // Validate input
        if (amount < 0)
            throw new ArgumentOutOfRangeException(nameof(amount), amount,
                "Damage amount must be non-negative.");

        // Log for debugging
        GD.Print("Player ", playerName, " taking ", amount,
            " damage. Current HP: ", hp);

        // Apply damage
        hp = Math.Max(0, hp - amount);

        // Conditional debug
#if DEBUG
        if (hp == 0)
        {
            GD.Print("Player died at position ", Position);
        }
#endif

        EmitSignal(SignalName.Damaged, amount);
    }
}
Output panelī redzami detalizēti debug ziņojumi; pie nederīgas ievades - error ar context.