Poradnik Developera
Poradnik ten opisuje, jak w prosty sposób rozbudowywać silnik gry, dodawać nowe zasoby wizualne, obiekty na mapie i modyfikować logikę.
1. Jak dodawać nowe modele 3D (.obj) i tekstury?
Silnik posiada zintegrowany własny loader plików .obj (przetwarzający wierzchołki i koordynaty UV) oraz parser obrazów.
Krok 1: Deklaracja wskaźników
W pliku include/Renderer.h w sekcji private: dodaj wskaźnik na nowy obiekt geometrii oraz zmienną przechowującą identyfikator tekstury (VRAM):
Krok 2: Ładowanie zasobów z dysku
W pliku src/Renderer.cpp, w metodzie Renderer::init(), wczytaj pliki (upewnij się, że są w folderze assets/):
myNewMesh = loadOBJ("assets/nowy_model.obj");
myNewTexture = loadTexture("assets/nowa_tekstura.png");
Krok 3: Rysowanie modelu w pętli renderującej
W metodzie Renderer::render(...) nałóż teksturę, przygotuj transformacje, narysuj model i posprzątaj:
glPushMatrix();
// 1. Przesuń w przestrzeni
glTranslatef(x, y, z);
// 2. Obróć (opcjonalnie)
glRotatef(angle, 0.0f, 1.0f, 0.0f);
// 3. Skaluj
glScalef(1.5f, 1.5f, 1.5f);
// 4. Aktywuj shader teksturowany i przypnij teksturę
texturedShader->use();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, myNewTexture);
// 5. Narysuj model z VRAM
myNewMesh->draw();
// 6. Posprzątaj stan
glDisable(GL_TEXTURE_2D);
texturedShader->stop();
glPopMatrix();
2. Tworzenie własnych klas i obiektów fizycznych
Każdy nowy, interaktywny obiekt na mapie (NPC, winda, przeciwnik) powinien dziedziczyć z abstrakcyjnej klasy bazowej GameObject lub Entity.
Przykład własnego przeciwnika
#include "Entity.h"
class EnemyBot : public Entity {
public:
EnemyBot(float startX, float startY, float startZ)
: Entity(startX, startY, startZ,
1.0f, 2.0f, 1.0f,
"WROG") {}
// Ta funkcja wywołuje się 60 razy na sekundę (Fixed Timestep)
void update(double fixedDeltaTime) override {
// Logika AI, np. ruch po osi X
x += 2.0f * fixedDeltaTime;
}
};
Po utworzeniu klasy zainicjuj jej instancję w Application, a następnie w głównej pętli odpytuj o kolizję z graczem za pomocą wbudowanej metody:
3. Rozszerzanie synchronizacji sieciowej
Jeśli wprowadzisz nową mechanikę, np. pasek zdrowia (HP) lub stany animacji gracza, musisz je synchronizować przez UDP.
Aktualizacja struktury
Otwórz include/PlayerData.h i dodaj nowe pole:
Aktualizacja interpolacji i odbioru
W NetworkManager.cpp, w metodzie receiveUpdates, przypisz odebrane wartości od przeciwnika (packet.payload.gameData) do jego wirtualnego odpowiednika po stronie Twojego klienta.
Obiekty PlayerData innych graczy w pętli renderowania będą teraz miały dostęp do nowej zmiennej i mogą np. rysować odpowiedni pasek zdrowia nad modelami.
4. Edycja proceduralnego generatora mapy
Klasa LevelGenerator odpowiada za budowanie trasy. Algorytm w pętli wylicza graniczne wartości skoku z fizyki gracza (playerMaxJump, playerMaxHoriz), a następnie rozrzuca przeszkody (Obstacle).
Możesz zmienić zachowanie generatora poprzez:
- zmianę liczby poziomów w górę (
layerCount), - zmianę gęstości platform (
blocksPerLayer), - modyfikację rozmieszczenia checkpointów (
isCheckpoint), - zmianę ścian ograniczających mapę (
isWall).
Generator na samym końcu rozstawia wokół mapy ściany z flagą isWall, uniemożliwiając wypadnięcie poza arenę w poziomie.
5. Dodawanie opcji i przycisków w Menu
Moduł MenuManager korzysta z maszyny stanów i listy wirtualnych przycisków.
Aby dodać nową zakładkę lub przycisk w menu (np. „Twórcy”):
Krok 1
W Protocol.h lub MenuManager.h dodaj nowy stan:
oraz nową akcję:
Krok 2
W pliku src/MenuManager.cpp, w metodzie loadButtonsForState(), dodaj nowy przycisk:
activeButtons.push_back({
centerX - 100, // X
startY + 140, // Y
200, 50, // Szerokość, Wysokość
"Tworcy Gry", // Tekst
MenuAction::OPEN_CREDITS,
0,
false
});
Krok 3
W Application.cpp, w pętli sprawdzającej kliknięcia (switch(action)), obsłuż akcję:
i przekaż informację do silnika, aby przełączył stan menu lub załadował odpowiedni plik wideo.
6. Odtwarzacz Wideo i Dialogi
Wideo
Klasa VideoPlayer potrafi dekodować pliki wideo na teksturę. Musisz pamiętać o wywoływaniu:
przed:
aby klatki aktualizowały się poprawnie w czasie (deltaTime).
Dialogi
Skrypty rozmów czytane są z pliku:
Jeśli chcesz dodać np. sekretne zakończenie, dopisz do pliku JSON nową strukturę z własną etykietą, a następnie załaduj ją w kodzie:
Silnik wyrenderuje rozmowę automatycznie, wyświetlając nakładkę z tekstem, z uwzględnieniem podziału na wiersze.