Przejdź do treści

MonoGame — jak napisałem grę platformową w C# bez silnika i bez wstydu

  • przez

Pewnego dnia postanowiłem napisać grę. Nie w Unity, nie w Godot, nie w Unreal — w czystym C#. Dlaczego? Bo jestem masochistą z dyplomem i chciałem wiedzieć, co tak naprawdę kryje się pod spodem każdego „przeciągnij sprite’a tutaj”.

Efekt? MathNerd — platformówka w stylu Mario, gdzie nerdziak w okularach i muszce musi pokonywać dresiarzy, rozwiązując zadania matematyczne. Tak, to gra edukacyjna. Nie, nie jestem z tego dumny. Tak, jest śmieszna.

Czym jest MonoGame?

MonoGame to open-source’owy framework do gier 2D/3D dla .NET — de facto następca XNA Microsoftu. Daje ci:

  • SpriteBatch do rysowania
  • Content Pipeline do zarządzania assetami (fonty, tekstury, dźwięki)
  • GameWindow, wejście z klawiatury/pada, zarządzanie pętlą gry

Czego nie daje: scen, komponentów, edytorów, asystentu AI, ani ręki do trzymania. To ty i Update(GameTime).

Jak to działa — architektura w 5 minutach

Cała gra żyje w kilku plikach .cs:

scripts/
├── Player.cs          — nerd z okularami (pixel art z prostokątów)
├── Enemy.cs           — dresiak ze snapbackiem (też pixel art)
├── Level.cs           — platformy, bloki, wrogowie
├── Physics.cs         — AABB, grawitacja
├── MathPopup.cs       — popup z pytaniem matematycznym + timer
├── MathChallengeManager.cs — generator pytań
├── HUD.cs             — życia (serduszka), wynik, prowokacje
└── Camera.cs          — scrolling poziomy

Nie ma tutaj żadnej magii. Jeden Texture2D o wymiarach 1×1 piksel, biały, i SpriteBatch.Draw(pixel, rect, color) — cała grafika to kolorowe prostokąty. Poważnie.

Pixel art bez obrazków

void D(int dx, int dy, int w, int h, Color c) =>
    sb.Draw(pixel, new Rectangle(bx + dx, by + dy, w, h), c);

// Twarz nerdziaka
D( 5,  4, 22, 18, skin);   // twarz
D( 3, 12, 12,  9, frm);    // lewa oprawka okularów
D( 4, 13, 10,  7, lens);   // lewy obiektyw
D( 6, 15,  4,  3, pup);    // źrenica

Wynik: postać z okularami, muszką i niebieskimi ogrodniczkami. Nie jest to Ori and the Blind Forest, ale ma swój urok.

Fizykalność bez silnika

// Oś X
pos.X += vel.X * dt;
// Rozwiąż kolizje...

// Oś Y
pos.Y += vel.Y * dt;
// Rozwiąż kolizje... (tutaj ustawiamy onGround = true)

Separacja osi X i Y to klasyczna technika AABB. Bez niej postać zacinałaby się w narożnikach platform jak twój kuzyn na rodzinnym obiedzie.

Pytania matematyczne

Kiedy gracz doskakuje na dresiarza, pojawia się popup:

=== MATH CHALLENGE ===
[ Algebra - face your destiny, mortal ]

ALGEBRA! The final boss of elementary school!
3x + 5 = 23
What is x?

[ _______________________ ]

Brain cells: [###]          ████████████░░░  12s

Masz 15 sekund i 3 próby. Jak nie zdążysz, dresiak wygrywa i tracisz życie. Matematyka boli podwójnie.

Coyote time i jump buffer

Dwa narzędzia każdego szanującego się platformera:

  • Coyote time (0.10s) — możesz skakać chwilę po tym, jak zejdziesz z platformy. Bo gracze są ludźmi, nie robotami.
  • Jump buffer (0.12s) — przytrzymanie skoku przed lądowaniem rejestruje skok. Eliminuje frustrację „przyszedłem za wcześnie”.
if (_jumpBufferTimer > 0 && _coyoteTimer > 0)
{
    Velocity.Y  = JumpVel;
    _jumpBufferTimer = 0;
    _coyoteTimer     = 0;
}

Wynik i jak go uruchomić

Repozytorium: github.com/letyshub/MathNerd

git clone https://github.com/letyshub/MathNerd
cd MathNerd
dotnet tool restore   # instaluje dotnet-mgcb
dotnet run

Wymagania: .NET 8 SDK. Brak Unity Hub, brak launcharów, brak 40 GB pobierania. Przyjemna odmiana.

Wnioski

MonoGame to nie silnik — to framework. Nie zrobi za ciebie nic, ale nie ograniczy cię też w niczym. Idealny jeśli:

  • Chcesz zrozumieć jak gry działają od środka
  • Lubisz C# i nie chcesz uczyć się nowego języka skryptowania
  • Tworzysz małą grę 2D bez potrzeby edytora poziomów
  • Masz zbyt dużo czasu i za mało snu

Jeśli oczekujesz klikania myszką i gotowych shaderów — wróć do Unity. Jeśli lubisz ref Vector2 i debugowanie kolizji o 2 w nocy — MonoGame czeka.