A prima vista sembra ragionevole utilizzare l'ereditarietà per separare diversi tipi di entità: questo è l'approccio "reale" a OOAD che la maggior parte delle università insegna in CS101. In altre parole, le classi sono denominate in base a oggetti del mondo reale che possiamo vedere: scrivania, cassettiera, porta, ecc.
Tuttavia, ciò ha poco senso nel contesto di un videogioco. Ci sono due categorie di preoccupazioni per gli oggetti che stai modellando:
-
Gli oggetti appaiono sullo schermo e devono essere disegnati, quindi ha senso modellarli usando le classi. Dopotutto hanno entrambe le proprietà (uno sprite o un modello 3D) e un comportamento (ad es. Movimento) che li rendono candidati per le classi.
-
Gli oggetti interagiscono. Un oggetto giocatore potrebbe interagire con un blocco di mattoni (dopo tutto questo è un clone di Mario) stando su di esso o distruggendolo. L'oggetto potrebbe interagire con un nemico calpestandolo.
Quando analizzo queste potenziali classi, vedo molte delle stesse preoccupazioni. Alcuni oggetti si muovono, altri no. Alcuni hanno "intelligenza" dietro di loro (AI o umano), altri no. Non vedo molto valore nella gerarchia che hai presentato: le preoccupazioni sono sparse troppo.
Raccomando di spostare le preoccupazioni su controllo oggetto al di fuori degli oggetti stessi. Ecco come lo progetterei, basato liberamente su MVC.
Attore sostituisce l'oggetto nella tua gerarchia. L'idea è che un attore abbia un qualche ruolo nel campo del gioco. Quel ruolo potrebbe essere "non fare nulla" nel caso di un mattone che non è smontabile e non esegue alcuna azione. Potrebbe essere in grado di rimuovere se stesso dal campo di gioco (il mattone viene distrutto). Potrebbe muoversi: potrebbe essere una piattaforma che pattuglia su un loop, un nemico con un'IA o un giocatore che riceve input da un dispositivo hardware.
ActorView viene passato a un attore e controlla come viene visualizzato. Questo potrebbe essere semplice come "Mostra sempre questo sprite" o "visualizza questo modello 3D in base a determinati criteri".
ActorController è il "cervello" dell'Actor. Si potrebbe avere un'implementazione "non fare nulla" che non cambia mai di stato (ad esempio un mattone infrangibile), un'implementazione semplice che può rimuovere l'attore (ad esempio un mattone fragile), un'implementazione più complessa (ad esempio una piattaforma che pattuglia avanti e indietro), un'implementazione AI (ad es. un nemico) o un giocatore (usa l'input hardware).
Ora puoi usare la composizione: l'attore dovrebbe davvero essere solo una classe che prende visione e un controller. ActorView potrebbe avere un piccolo numero di sottoclassi che possono essere riutilizzate. Hai davvero bisogno di una vista diversa per ogni mattone statico? No, puoi semplicemente riutilizzare una classe e passare una bitmap diversa ogni volta. ActorController potrebbe avere un piccolo numero di sottoclassi per ogni tipo di attore: statico, con script (mosse, pause), AI, giocatore.