Skocz do zawartości

Organizacja kodu w grach - proste gamemakerowe obiekty


CrackGM

Rekomendowane odpowiedzi

Ponieważ wiele osób ma problemy z organizacją kodu w grach pisanych poza GM i często stosują artystyczny nieład w którym później sami nie mogą się połapać postanowiłem napisać krótki tutorial który może pomóc.

Zapewne wielu bardziej doświadczonych koderów gier ode mnie wyśmieje ten sposób - z góry mówię nie jest to najlepszy sposób - ma on na celu pomóc przemyśleć organizację w kodzie.

 

A więc gwoździem programu są klasy i dziedziczenie które musicie opanować. (http://pl.wikibooks.org/wiki/C%2B%2B/Czym_jest_obiekt oraz http://pl.wikibooks.org/wiki/C%2B%2B/Dziedziczenie), sam mistrzem nie jestem, często zaglądam do pomocy online ale podstawy trzeba znać.

 

Nie będę tutaj się rozpisywał, lepiej przejdę do konkretów.

 

Najpierw musimy sobie zrobić "szkielet" naszych przyszłych gjeemowych obiektów:

class Object {
public:
    int myID;
    bool exists;
    virtual void onCreate()=0;
    virtual void onStep()=0;
    virtual void onRender()=0;
    virtual void onDestroy()=0;
};

Jak każdy GMowy obiekt mamy eventy, dlatego jest to umieszczone w szkielecie (każdy obiekt ma eventy - tutaj pokazałem przykładowe).

Zwróćcie uwagę na to że metody wewnątrz szkieletu są abstrakcyjne, jest to ważne. Zmienna booleanowa exists jest pierdołą która może się przydać później, oczywiście pokażę to niżej. Warto również zapisać sobie ID obiektu, które zapodamy później w naszym vectorze, który będzie nam przechowywał stworzone już obiekty, który definiujemy w ten sposób:

vector<Object*> objects;

Vector nie przechowuje klas abstrakcyjnych...ale może przechowywać wskaźniki na nie.

 

To teraz możemy dodać trochę kolejnych pierdół do naszego kodu typu:

int ids = 0;
bool gameIsOn = true;

Zalecam zadeklarować to jako zmienne globalne.

 

Teraz definicja naszego obiektu:

class oPlayer : public Object {
public:
    virtual void onCreate();
    virtual void onStep();
    virtual void onRender();
    virtual void onDestroy();

    int health; // nasze zmienne
};

void oPlayer::onCreate() {
    health = 10;
}

void oPlayer::onStep() {
    health -= 1;
    if (health <= 0) {
        onDestroy();
    }
}

void oPlayer::onRender() {
    cout << health << endl;
}

void oPlayer::onDestroy() {
    gameIsOn = false;
    exists = false;
}

Jeżeli rozumiecie klasy i dziedziczenie to nie ma co tutaj się rozpisywać... raczej wszystko jest jasne :)

 

Teraz najważniejsza część kodu - pętla główna gry itp:

 

int main() {
    int playerID = ids; // jak przechowywać ID playera
    objects.push_back(new oPlayer()); //dodajemy nowy obiekt do vectora
    ids ++; //i zapisujemy sobie ile to już mamy obiektów.

        //obsługa eventów - onCreate - wykonywane tylko raz. Oczywiście onCreate można zastąpić sobie zwykłym konstruktorem (w sumie jest niepotrzebne, lepiej użyć konstruktor - wtedy jest łatwiej jeżeli tworzymy obiekty w toku gry)
    for (int i = 0; i < ids; i++) {
        objects[i]->myID = i; // zapisujemy sobie id obiektu z vectora 
        objects[i]->onCreate();
    }

        //pętla głowna gry - tutaj wszystko się "dzieje"
    while(gameIsOn) {
                //znów obsługa eventów...onStep
        for (int i = 0; i < ids; i++) {
            if (objects[i]->exists) {
                objects[i]->onStep();
            }
        }
                //...i onRender
        for (int i = 0; i < ids; i++) {
            if (objects[i]->exists) {
                objects[i]->onRender();
            }
        }
    }
    cout << "Game End." << endl;
    system("pause");
    return 0;
}

Mam nadzieję że wszystko jest w miarę jasne.

Znów podkreślam że tutorial nie jest "sztywnym" szkieletem dla twórców gier - to jest tylko najprostszy przykład organizacji obiektów/kodu, dla początkujących koderów gier. Sam na tym działam...no i działa :D

 

Cały kod:

#include <iostream>
#include <vector>

using namespace std;

class Object {
public:
    int myID;
    bool exists;
    virtual void onCreate()=0;
    virtual void onStep()=0;
    virtual void onRender()=0;
    virtual void onDestroy()=0;
};

vector<Object*> objects;
int ids = 0;
bool gameIsOn = true;

class oPlayer : public Object {
public:
    virtual void onCreate();
    virtual void onStep();
    virtual void onRender();
    virtual void onDestroy();
    int health;
};

void oPlayer::onCreate() {
    health = 10;
}

void oPlayer::onStep() {
    health -= 1;
    if (health <= 0) {
        onDestroy();
    }
}

void oPlayer::onRender() {
    cout << health << endl;
}

void oPlayer::onDestroy() {
    gameIsOn = false;
    exists = false;
}


int main() {
    int playerID = ids;
    objects.push_back(new oPlayer());
    ids ++;

    for (int i = 0; i < ids; i++) {
        objects[i]->myID = i;
        objects[i]->onCreate();
    }

    while(gameIsOn) {
        for (int i = 0; i < ids; i++) {
            if (objects[i]->exists) {
                objects[i]->onStep();
            }
        }
        for (int i = 0; i < ids; i++) {
            if (objects[i]->exists) {
                objects[i]->onRender();
            }
        }
    }
    cout << "Game End." << endl;
    system("pause");
    return 0;
}

 

(polecam również każdy obiekt zapodać sobie w oddzielnym headerze (dla mało wiedzących *.h))

 

I tak nikt nie doceni, ale przynajmniej jest. xD

Odnośnik do komentarza
Udostępnij na innych stronach

niestety, ale nie doceniam.

brak generycznego sposobu tworzenia game objectow, do tego generowanie identyfikatorow na poczatku programu, co powoduje ze nie mozna dodawac instancji w locie (bullety, itp.).

Odnośnik do komentarza
Udostępnij na innych stronach

w takim razie pokaz im jak to zrobic, skoro to banalne :)

Odnośnik do komentarza
Udostępnij na innych stronach

@Pasztet: setParent() nie powinno byc w ogole publiczne - stwarzasz tym mozliwosc zepsucia apki przez uzytkownika.

Odnośnik do komentarza
Udostępnij na innych stronach

#include <iostream>
#include <vector>

using namespace std;

class Object {
public:
    bool exists;
    virtual void onStep()=0;
    virtual void onRender()=0;
    virtual void onDestroy()=0;
    Object() { exists = true; }
};

vector<Object*> objects;
int ids = 0;
bool gameIsOn = true;

class oPlayer : public Object {
public:
    oPlayer();
    virtual void onStep();
    virtual void onRender();
    virtual void onDestroy();
    int health;
};

oPlayer::oPlayer() {
    health = 10;
}

void oPlayer::onStep() {
    health -= 1;
    if (health <= 0) {
        onDestroy();
    }
}

void oPlayer::onRender() {
    cout << health << endl;
}

void oPlayer::onDestroy() {
    gameIsOn = false;
    exists = false;
}

int findFreeID() {
    for (size_t i = 0; i < objects.size(); i++) {
        if (!objects[i]->exists) return i;
    }
    return -1;
}

int newObject(Object* obj) {
    int id = findFreeID();
    if(id != -1) {
        objects[id] = obj;
    } else {
        objects.push_back(obj);
        id = objects.size()-1;
    }
    return id;
}

int main() {
    int playerID = newObject(new oPlayer());
    cout << "Player ID: " << playerID << endl;

    while(gameIsOn) {
        for (size_t i = 0; i < objects.size(); i++) {
            if (objects[i]->exists) {
                objects[i]->onStep();
            }
        }
        for (size_t i = 0; i < objects.size(); i++) {
            if (objects[i]->exists) {
                objects[i]->onRender();
            }
        }
    }
    cout << "Game End." << endl;
    system("pause");
    return 0;
}

V2 + Dodawanie obiektów w locie :3

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...