Come organizzare un motore di gioco in C ++? Il mio uso dell'eredità è una buona idea?


11

Sono un principiante sia nello sviluppo del gioco che nella programmazione.

Sto cercando di imparare alcuni principi nella costruzione di un motore di gioco.
Voglio creare un gioco semplice, sono nel punto in cui sto cercando di implementare il motore di gioco.

Quindi ho pensato che il mio motore di gioco dovesse controllare queste cose:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

Ho progettato i miei oggetti in questo modo:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

e ho 4 oggetti diversi nel mio gioco: aereo, ostacolo, giocatore, proiettile e li abbiamo progettati in questo modo:

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

Ora nel motore di gioco voglio avere un elenco di oggetti generale in cui posso verificare, ad esempio, la collisione, spostare oggetti e così via, ma voglio anche che il motore di gioco prenda i comandi utente per controllare il giocatore ...

E 'questa una buona idea?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

il costruttore GameEngine sarà così:

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

Il fatto che sto usando un puntatore è perché devo chiamare quello fire()che è unico per l'oggetto Player.

Quindi la mia domanda è: è una buona idea? Il mio uso dell'eredità è sbagliato qui?



Li ho visti dopo la pubblicazione e in realtà la composizione è un'ottima idea! Ho sempre paura di finire con c con le classi invece di avere un vero design c ++!
Pella86,

Penso che ci siano molte più ragioni per usare la composizione rispetto all'ereditarietà ... La mia risposta fornisce alcuni esempi sui vantaggi di flessibilità che offre. Per un buon esempio di implementazione, consulta gli articoli che ho collegato.
Coyote

Risposte:


12

Dipende da cosa intendi per "buono". Farà il lavoro fatto, certamente. Ha molti lati positivi e negativi. Non li troverai finché il tuo motore non sarà molto più complesso. Vedi questa domanda su SO , per la discussione sull'argomento.

Ci sono molte opinioni in entrambi i casi, ma se il codice del tuo motore è ancora in quella fase iniziale, sarà difficile spiegarti perché potrebbe essere negativo.

In generale, Scott Meyers ha alcune grandi cose da dire sull'eredità. Se sei ancora in quella fase di sviluppo, leggi questo libro (C ++ efficace) prima di continuare. È un libro fantastico ed è un requisito per tutti i programmatori che lavorano nella nostra azienda. Ma per riassumere il consiglio: se stai scrivendo una classe e stai pensando di usare l'eredità, sii assolutamente chiaro che la relazione tra la classe e il suo antenato è una relazione "è un". Cioè, ogni B è un A. Non solo usare l'ereditarietà come un modo semplice per dare a B l'accesso alla funzionalità fornita da A, in questo modo porterà a gravi problemi architetturali e ci sono altri modi per farlo.

In poche parole; per un motore di qualsiasi dimensione, scoprirai rapidamente che gli oggetti raramente cadono in una struttura gerarchica ordinata per la quale funziona l'ereditarietà. Usalo quando è appropriato, ma non cadere nella trappola di usarlo per tutto - impara altri modi per mettere in relazione gli oggetti.


Secondo l'opinione dell'eredità. La mia regola personale è: non farlo a meno che non sia necessario o farlo in altro modo sarebbe stupido. In altre parole, l'eredità è una cattiva idea nel 99% dei casi (almeno :)). Inoltre, potrebbe essere utile separare la logica di gioco (ruota / sposta) dal rendering (generate_mesh ()). Un'altra cosa è fire () - considera di avere un elenco di azioni e una funzione di azione generica (my_action) - in questo modo non finirai con un bazillion di funzioni diverse distribuite in tutte le classi. In entrambi i casi, mantienilo semplice e rifletti spietatamente quando incontri problemi.
Gilead,

8

Per i dati logici e le funzioni dei tuoi oggetti di gioco (entità) consiglio vivamente di usare la composizione sull'ereditarietà. Un buon inizio sarebbe l'approccio basato sui componenti. È ben descritto in questo articolo di Cowboy Programming che descrive come questa architettura può essere implementata e come ha beneficiato della serie di Tony Hawk.

Anche questo articolo sui sistemi di entità mi ha ispirato quando l'ho letto per la prima volta.

L'ereditarietà è un ottimo strumento per il polimorfismo. Ad eccezione di progetti molto semplici, dovresti evitare di usarlo come un modo per strutturare le entità e le logiche di gioco. Se vai con l'eredità, finirai sicuramente con la necessità di replicare e conservare copie dello stesso codice per implementare funzioni che non possono essere ereditate da un genitore comune. E probabilmente inizierai a inquinare le tue classi con logiche e dati più comunemente usati per evitare di replicare troppo codice.

I componenti sono molto utili in numerose situazioni. Puoi ottenere molto di più usandoli:

Flessibilità con i tipi di entità : ci sono momenti nello sviluppo del gioco in cui alcune entità si evolvono improvvisamente e mentre il progettista del gioco prende la decisione dello sviluppatore dovrà implementarlo. Bastano pochi passaggi per aggiungere un comportamento esistente senza replicare il codice. I sistemi basati su componenti consentono a più non programmatori di apportare ulteriori modifiche. Ad esempio, ogni tipo di entità anziché essere rappresentato da una classe può essere rappresentato da un file di descrizione che contiene tutti i nomi dei componenti e i parametri necessari per configurare il tipo. Ciò consente ai non programmatori di eseguire e testare le modifiche senza ricompilare il gioco. La semplice modifica dei file di descrizione sarà sufficiente per aggiungere, rimuovere o modificare i componenti utilizzati da ciascun tipo di entità.

Flessibilità con casi particolari e scenari specifici : ci sono casi in cui è necessario che il giocatore sia in grado di equipaggiare armi o oggetti, è possibile aggiungere un componente di inventario al giocatore. durante lo sviluppo del tuo gioco avrai bisogno di altre entità per avere un inventario (nemici per esempio). E ad un certo punto hai nel tuo scenario una situazione in cui un albero dovrebbe contenere una sorta di oggetto nascosto. Con i componenti è possibile aggiungere un componente di inventario anche a entità il cui tipo non è stato progettato originariamente per averne uno senza ulteriori sforzi di programmazione.

Flessibilità in fase di esecuzione : i componenti offrono un modo molto efficace per modificare il comportamento delle entità in fase di esecuzione. È possibile aggiungere e rimuovere componenti in fase di esecuzione e quindi creare o rimuovere comportamenti e proprietà quando necessario. Consentono inoltre di separare diversi tipi di dati in gruppi che possono (a volte) essere calcolati separatamente in parallelo su più GPU.

Flessibilità nel tempo : i componenti possono essere utilizzati per mantenere la compatibilità tra le versioni. Ad esempio, quando vengono apportate modifiche tra le versioni di un gioco è possibile (non sempre) semplicemente aggiungere nuovi componenti che implementano i nuovi comportamenti e le modifiche. Quindi il gioco creerà un'istanza dei componenti giusti collegati alle entità in base al file di salvataggio caricato, peer connesso o al server al quale il giocatore si unisce. Ciò risulta estremamente utile in scenari in cui non è possibile controllare le date di rilascio della nuova versione su tutte le piattaforme (Steam, Mac App Store, iPhone, Android ...).

Per una migliore comprensione dare un'occhiata alle domande taggate [basate su componenti] e [sistema-entità] .


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.