Voglio liberarmi del mio modello di design che rende tutto statico e globale, ma come?


11

Sto realizzando un piccolo cingolato sotterraneo nello spazio e mi piacerebbe sentire alcuni consigli su come rendere più piacevole il backend del motore. Fondamentalmente, attualmente tutto si basa su un crapload di manager:

  • BackgroundManager: ha un AddBackground(image, parallax)metodo per creare fantastici effetti di sfondo.
  • ConfigManager: legge / crea il file di configurazione e contiene anche i dati letti da quel file di configurazione.
  • DrawManager: ha un Draw(sprite)metodo per disegnare elementi sullo schermo e cose come SetView(view), GetView()ecc.
  • EntityManager: contiene tutte le entità attive e ha metodi per aggiungere, rimuovere e trovare entità.
  • DungeonManager (in realtà chiamato GridManager, ma questo è per semplicità): ha metodi come GenerateDungeon(), PlaceEnemies(), PlaceFinish()etc.

Ora non sono nemmeno a metà strada per elencare tutti i miei manager, quindi penso che questo debba cambiare. Il mio gioco si basa anche su Schermate, il che lo rende più fastidioso perché non ho bisogno della metà delle cose che i miei manager gestiscono, ad esempio nel menu principale (Non ho bisogno di fisica / entità / prigioni nel mio menu principale !)

Ora ho pensato di rendere i gestori non statici e di dare al mio ScreenMainGame un'istanza di tutti i gestori specifici del gioco. Ma questo rende la chiamata / ottenere qualsiasi cosa relativa ai gestori un casino enorme ... per esempio, se volessi disegnare la mia prigione da qualsiasi posto che non si trova ScreenMainGame.Draw(), dovrei farlo ...

((ScreenMainGame)ScreenManager.CurrentScreen).GridManager.Draw()

È davvero brutto.

Quindi, compagni sviluppatori di giochi, qualcuno di voi ha un'idea su come risolvere questo casino? Sarei apprezzato!


Non sono sicuro di come aiutarti, ma consiglierei di leggere un buon libro sui modelli di design. Ho letto Head First Design Patterns ed è molto facile da capire. Gli esempi sono in Java, ma penso che sarebbe abbastanza vicino a C # per aiutarti. Scusate se il mio suggerimento è troppo alle prime armi.
Amplifica il

Cosa stai usando per interfacciarsi con la scheda grafica? XNA? SlimDX?
BlueRaja - Danny Pflughoeft,

@BlueRaja: sto usando SFML.NET.
Dlaor,

In tal caso, dovresti semplicemente utilizzare i normali metodi per affrontare questi problemi in C #: vedi il mio ultimo link di seguito, così come Cos'è l'iniezione di dipendenza?
BlueRaja - Danny Pflughoeft,

Risposte:


10

Che dire di un motore basato su componenti ?

Avresti una classe principale denominata Engine, che manterrebbe un elenco di GameScreens, che a sua volta avrebbe un elenco di Components.

Il motore ha un Updatee un Drawmetodo ed entrambi chiamano GameScreen"s" Updatee Drawmetodi, che a loro volta passano attraverso ogni componente e chiamano Updatee Draw.

Presentato in questo modo, concordo sul fatto che sembra un design scadente e ripetitivo. Ma credimi, il mio codice è diventato molto più pulito utilizzando un approccio basato sui componenti rispetto a tutte le mie vecchie classi manager .

È anche molto più semplice mantenere tale codice, poiché stai attraversando una gerarchia di grandi classi e non devi cercare BackgroundManagertutti i diversi sfondi specifici. Devi solo una ScrollingBackground, ParallaxBackground, StaticBackground, ecc, che derivano tutte da una Backgroundclasse.

Alla fine costruirai un motore piuttosto solido che puoi riutilizzare su tutti i tuoi progetti con molti componenti e metodi di supporto utilizzati di frequente (ad esempio FrameRateDisplayercome utilità di debug, una Spriteclasse come uno sprite di base con una trama e metodi di estensione per i vettori e generazione di numeri casuali).

Non avresti più una BackgroundManagerclasse, ma una Backgroundclasse che si gestirà da sola.

All'inizio del gioco, tutto ciò che devi fare è sostanzialmente questo:

// when declaring variables:
Engine engine;

// when initializing:
engine = new Engine();
engine.Initialize();
engine.LoadContent();
engine.AddGameScreen(new MainMenuScreen());

// when updating:
engine.Update();

// when drawing:
engine.Draw();

E questo è tutto per il codice di inizio del gioco.

Quindi, per la schermata del menu principale:

class MainMenuScreen : MenuScreen // where MenuScreen derives from the GameScreen class
{
    const int ENEMY_COUNT = 10;

    StaticBackground background;
    Player player;
    List<Enemy> enemies;

    public override void Initialize()
    {
        background = new StaticBackground();
        player = new Player();
        enemies = new List<Enemy>();

        base.AddComponent(background); // defined within the GameScreen class
        base.AddComponent(player);
        for (int i = 0; i < ENEMY_COUNT; ++i)
        {
            Enemy newEnemy = new Enemy();
            enemies.Add(newEnemy);
            base.AddComponent(newEnemy);
        }
    }
}

Hai un'idea generale.

Manterrai anche il riferimento Engineall'interno di tutte le tue GameScreenclassi, per poter aggiungere nuove schermate anche all'interno di una GameScreenclasse (ad es. Quando l'utente fa clic sul pulsante StartGame mentre sei all'interno di te MainMenuScreen, puoi passare a GameplayScreen).

Lo stesso vale per la Componentclasse: dovrebbe contenere il riferimento del suo genitore GameScreen, per avere sia l'accesso alla Engineclasse sia il suo genitore GameScreenper aggiungere nuovi componenti (ad esempio puoi creare una classe relativa a HUD chiamata DrawableButtonche contiene un DrawableTextcomponente e un StaticBackgroundcomponente).

Successivamente puoi anche applicare altri modelli di progettazione, come il "modello di progettazione del servizio" (non sono sicuro del nome esatto) in cui puoi mantenere diversi servizi utili all'interno della tua Engineclasse (semplicemente tieni un elenco di se IServicelasci che altre classi aggiungano servizi da soli ). ad es. manterrei un Camera2Dcomponente su tutto il mio progetto come servizio per applicare la sua trasformazione quando disegna altri componenti. Questo evita di doverlo passare come parametro ovunque.

In conclusione, potrebbero sicuramente esserci altri progetti migliori per un motore, ma ho trovato il motore proposto da questo link molto elegante, estremamente facile da mantenere e riutilizzabile. Personalmente consiglierei almeno di provarlo.


1
XNA supporta tutto questo out-of-the-box via se GameComponente servizi. Non c'è bisogno di una Engineclasse separata .
BlueRaja - Danny Pflughoeft,

+1 per questa risposta. Inoltre, avrebbe senso inserire il disegno in un thread separato dal thread di aggiornamento del gioco?
f20k,

1
@BlueRaja - DannyPflughoeft: In effetti, ma supponevo che XNA non fosse usato, quindi ho spiegato un po 'di più su come farlo.
Jesse Emond,

@ f20k: Sì, più o meno allo stesso modo di XNA. Non so come sia implementato però. = / Ci deve essere un articolo da qualche parte su Internet su come farlo. :)
Jesse Emond,

Sì, non sto usando XNA ma questa sembra una bella alternativa a quello che sto facendo. Attualmente sto leggendo il libro "Head First Design Patterns" per vedere se ci sono altre alternative. Saluti!
Dlaor,


-3

Solo perché sono statici o globali non significa che devono sempre essere caricati / inizializzati. Utilizzare un modello Singleton e consentire ai gestori di essere liberi e ricaricati su richiesta.


4
Un singleton è solo una facciata per il problema di fondo che l'OP sta cercando di risolvere qui.
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.