4.6 Noslēguma projekts: Top-down šautene
Stundas uzdevums: Apvienot datu struktūras, AI un object pooling pilnvērtīgā top-down šautenē.
80 min plāns: Ievads un kritēriju pārruna (~10 min) · 1. uzdevums (~20 min) - spēlētājs ar kustību un mērķēšanu · 2. uzdevums (~25 min) - divi ienaidnieku tipi ar AI · 3. uzdevums (~20 min) - viļņu sistēma un rezultāts · Iesniegšana (~5 min).
Pirms sāc: šis ir 4. tēmas noslēguma projekts. Izmanto visu apgūto: sarakstus, vārdnīcas, stāvokļu mašīnu, pathfinding un lādiņu krātuvi. Sāc ar VIENU ienaidnieka tipu.
Teorija: Spēles apraksts un arhitektūra
Top-down šautene ar šādām funkcijām:
- Spēlētājs ar 8 virzienu kustību un mouse-aimed šaušanu.
- 3 ienaidnieku tipi ar dažādu AI uzvedību.
- Pathfinding ap šķēršļiem.
- Wave system: ienaidnieku viļņi ar augošu sarežģītību.
- Pickup sistēma: HP, ammo, power-ups.
- Score un wave skaitītājs.
// Klašu hierarhija
public partial class Player : CharacterBody2D { /* WASD + shoot */ }
public partial class Enemy : CharacterBody2D { /* state machine + pathfinding */ }
public partial class FastEnemy : Enemy { /* fast, light */ }
public partial class Brute : Enemy { /* slow, strong */ }
public partial class Shooter : Enemy { /* keeps distance, shoots */ }
public partial class Bullet : Area2D { /* pooled */ }
public partial class WaveManager : Node { /* spawns waves */ }
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: C# kodā pamatoti izvēlēti List, Dictionary vai HashSet, AI stāvokļi ir skaidri, un sarežģītākas sistēmas var profilēt vai atkļūdot.
public class AiDataCheckpoint
{
public string Lesson = "4.6 Noslēguma projekts: Top-down šautene";
public bool UsesCSharp = true;
public bool ChoosesContainer = true;
public bool SeparatesAiState = true;
public bool HandlesRemovalSafely = true;
}
Praktiskie uzdevumi
1. uzdevums -
Spēlētājs ar kustību un mērķēšanu
Beigās spēlētājs pārvietosies ar WASD un šaus peles virzienā.
- Izveido spēlētāja klasi ar 8 virzienu kustību un normalizētu vektoru.
- Paņem peles pozīciju ar
GetGlobalMousePosition(). - Pagriez spēlētāju pret peli ar
LookAt(...). - Pievieno šaušanu no 4.5 stundas ar lādiņu krātuvi.
- Izšauj lādiņu spēlētāja skatīšanās virzienā.
- Būvē un pārbaudi kustību visos virzienos un šaušanu.
- Pārliecinies, ka diagonālā kustība nav ātrāka.
Gatavs, kad: spēlētājs kustas visos virzienos, vienmēr skatās uz peli un šauj tieši tajā virzienā.
2. uzdevums -
Divi ienaidnieku tipi ar AI
Beigās ienaidnieki uzvedīsies atšķirīgi.
- Izveido bāzes klasi
Enemyar stāvokļu mašīnu no 4.3 stundas. - Izveido bērna klasi
Chaser, kas dzenas ar pathfinding. - Izveido otru bērna klasi
Shooter, kas apstājas un šauj no attāluma. - Glabā visus ienaidniekus vienā
List<Enemy>. - Uzraksti vienu ciklu, kas atjaunina visus ienaidniekus.
- Pievieno ienaidniekiem HP un dzēšanu, kad tie tiek nošauti.
- Pārbaudi, ka abi tipi uzvedas atšķirīgi.
Gatavs, kad: viens ienaidnieku tips dzenas pakaļ, otrs apstājas un šauj, un abus apstrādā viens cikls.
3. uzdevums -
Viļņu sistēma un rezultāts
Beigās spēle kļūs arvien grūtāka un skaitīs punktus.
- Izveido klasi
WaveManagerar laukuint wave = 1;. - Radi katrā vilnī vairāk ienaidnieku nekā iepriekšējā.
- Pārej uz nākamo vilni, kad visi ienaidnieki iznīcināti.
- Pievieno punktu skaitītāju un HUD ar vilni, punktiem un HP.
- Pievieno spēles beigas, kad spēlētāja HP sasniedz nulli.
- Izspēlē vismaz trīs viļņus.
- Veic commit un push.
Gatavs, kad: spēle pāriet no viļņa uz vilni, katrā ir vairāk ienaidnieku, un beidzas, kad spēlētājs zaudē HP.
Papildu uzdevums - Pievieno savācamos bonusus
Ja pamatdarbs ir gatavs, ļauj spēlētājam atjaunot HP un munīciju.
- Izveido
Area2Dbonusa scēnu ar veiduHPvaiAMMO. - Radi bonusu nejaušā vietā pēc katra viļņa.
- Palielini attiecīgo vērtību, kad spēlētājs to savāc.
- Ierobežo HP, lai tas nepārsniegtu maksimumu.
- Pārbaudi abus bonusa veidus.
Gatavs, kad: bonusi parādās pēc katra viļņa un atjauno HP vai munīciju, nepārsniedzot maksimumu.
Ko sagatavo
Stundas mērķis: Tev ir izspēlējama top-down šautene: spēlētājs ar 8 virzienu kustību un mērķēšanu pret peli, vismaz divi ienaidnieku tipi ar atšķirīgu AI, viļņu sistēma ar augošu grūtību un HUD ar punktiem un HP.
GitHub krātuvē jābūt:
- Viss projekts ar C# skriptiem, scēnām un projekta failiem.
- README.md ievietota video saite, kurā izspēlē vismaz divus viļņus un redzami abi ienaidnieku tipi.
- Faili
Player.cs,Enemy.cs, abu ienaidnieku tipu klases unWaveManager.cs. - README.md: kā tavi divi ienaidnieku tipi atšķiras un kāpēc abus var apstrādāt vienā ciklā.
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
- Lag ar daudziem enemies: Pārliecinies, ka NavigationAgent2D update_freq ir saprātīgs.
- Bullets sit savus: Pārbaudi collision layers - Player bullets jāignore Player.
- Wave neuzsākas: Timer vai counter sajaucies - debug ar print statements.
Godot ekrānuzņēmumi
Koda piemērs (paplašināts)
using Godot;
using System.Collections.Generic;
public enum EnemyType { Fast, Brute, Shooter }
public partial class WaveManager : Node
{
private int currentWave = 0;
private float waveTimer = 0.0f;
private readonly List<Enemy> activeEnemies = new();
private record Wave(int FastEnemies, int Brutes, int Shooters);
private readonly List<Wave> waves = new()
{
new Wave(3, 0, 0), // wave 1
new Wave(5, 1, 0), // wave 2
new Wave(6, 2, 1), // wave 3
new Wave(8, 3, 2), // wave 4
new Wave(10, 5, 3) // wave 5 + boss
};
public override void _Process(double delta)
{
waveTimer -= (float)delta;
if (activeEnemies.Count == 0 && waveTimer <= 0)
{
StartNextWave();
}
}
private void StartNextWave()
{
if (currentWave >= waves.Count)
{
GD.Print("All waves complete!");
return;
}
Wave wave = waves[currentWave];
for (int i = 0; i < wave.FastEnemies; i++) Spawn(EnemyType.Fast);
for (int i = 0; i < wave.Brutes; i++) Spawn(EnemyType.Brute);
for (int i = 0; i < wave.Shooters; i++) Spawn(EnemyType.Shooter);
currentWave++;
waveTimer = 30.0f;
}
}