Progettazione di un motore flessibile basato su piastrelle


8

Sto cercando di creare un motore di gioco flessibile basato su tessere per creare tutti i tipi di giochi di puzzle non in tempo reale, proprio come Bejeweled, Civilization, Sokoban e così via.

Il primo approccio che ho avuto è stato quello di avere una matrice 2D di oggetti Tile, e quindi avere classi ereditate da Tile che rappresentavano gli oggetti di gioco. Sfortunatamente in questo modo non ho potuto impilare più elementi di gioco sulla stessa tessera senza avere un array 3D.

Poi ho fatto qualcosa di diverso: avevo ancora l'array 2D di oggetti Tile, ma ogni oggetto Tile conteneva un Elenco in cui ho inserito e entità diverse. Funzionava bene fino a 20 minuti fa, quando mi sono reso conto che è troppo costoso fare molte cose, guarda questo esempio:

Ho un'entità Wall. Ogni aggiornamento devo controllare le 8 tessere adiacenti, quindi controllare tutte le entità nell'elenco delle tessere, controllare se una di quelle entità è un muro, quindi disegnare lo sprite corretto. (Questo viene fatto per disegnare muri uno accanto all'altro senza soluzione di continuità)

L'unica soluzione che vedo ora è avere un array 3D, con molti livelli, che potrebbe adattarsi ad ogni situazione. Ma in questo modo non posso impilare due entità che condividono lo stesso livello sulla stessa tessera. Ogni volta che voglio farlo, devo creare un nuovo livello.

C'è una soluzione migliore? Cosa faresti?


In che modo l'utilizzo di un array 3D risolve il problema con quella situazione di controllo del muro. Non sarebbe lo stesso?
Michael Coleman,

Saprei che i muri rimangono solo sul livello numero 1. Quindi posso solo fare: Piastrelle [Wall.X - 1, Wall.Y, 1] è Wall?
Vee

Quindi non puoi semplicemente controllare il primo elemento nell'elenco? Non vedo come un elenco solleva un problema.
Michael Coleman,

Il muro può essere in qualsiasi punto dell'elenco con il secondo approccio. Devo passare in rassegna tutte le Entità e verificare se si tratta di un muro.
V,

4
In realtà hai avuto un problema di prestazioni o sei solo preoccupato di farlo? L'hai profilato?
munifico

Risposte:


2

Hai effettivamente visto questo problema o l'hai appena pensato? Perché non riesco a vedere come la ripetizione dell'elenco degli oggetti abbia un effetto evidente sulle prestazioni. Dovresti avere centinaia di oggetti per tessera perché questo abbia importanza. La maggior parte dei giochi che mi viene in mente hanno 10, top.

Se hai davvero un problema, l'idea migliore è memorizzare nella cache i dati, non modificare la rappresentazione. Nel tuo esempio, la configurazione del muro probabilmente non cambia ogni frame. Basta inserire nella cache il tipo di sprite corretto, non ricalcolarlo costantemente.

Se vuoi creare un gioco pazzo che ha molti oggetti e cambiano continuamente, puoi creare diversi livelli con semantica diversa. Ad esempio, il livello muro contiene solo muri, non più di 1 per piastrella e, diciamo, il livello decorazione contiene elenchi di oggetti che non cambiano mai.

Infine, se vuoi avere un'architettura ultra-flessibile che supporti idealmente ogni possibile gioco - sfortuna, non esiste nulla del genere.


2

Due suggerimenti. Innanzitutto, dovresti risolvere lo sprite con cui ogni tessera verrà disegnata durante il caricamento della mappa / livello delle tessere. Dovrebbe essere abbastanza facile attraversare l'array 2D quando viene caricato il tuo livello e decidere che una parete certiana deve essere una forma a L e un'altra deve essere una | forma.

In secondo luogo, memorizzerei i dati statici relativi a una piastrella in un posto diverso rispetto ai dati attivi. Quindi un oggetto a tessera può avere un elenco di oggetti che vanno e vengono dalla tessera, ma non è necessario attraversarlo per vedere se, ad esempio, vogliamo solo sapere se la tessera è percorribile o meno.

Questo codice psudo è più o meno come mi sono avvicinato a un sistema basato su piastrelle.

Tile {
  Texture tex;
  //...other static data...
  List currentObjects;
}

Tile t = Tiles[x][y];

Questo presuppone che ci siano tessere statiche nel tuo gioco, ma questo è un presupposto abbastanza buono in molti giochi basati su tessere in cui hai a che fare con muri e simili.


1

Tu hai detto:

Dovevo avere una matrice 2D di oggetti Tile e quindi classi ereditate da Tile che rappresentavano gli oggetti di gioco. Sfortunatamente in questo modo non ho potuto impilare più elementi di gioco sulla stessa tessera senza avere un array 3D.

Poi ho fatto qualcosa di diverso: avevo ancora l'array 2D di oggetti Tile, ma ogni oggetto Tile conteneva un Elenco in cui ho inserito e entità diverse. Funzionava bene fino a 20 minuti fa, quando mi sono reso conto che è troppo costoso fare molte cose

Questa è esattamente la situazione che si desidera risolvere usando il modello MVC (model view controller) per separare il modello dalla propria vista. MVC pone questo problema in due parti:

  • Come modellare una pila di tessere
  • Come visualizzare una pila di tessere

Invece di avere una matrice 2D di tessere o una matrice 2D di un elenco di tessere, il tuo modello memorizzerebbe una matrice 2D di oggetti di gioco. Quindi il tuo array 2D di classi di tessere guarderà gli oggetti di gioco corrispondenti e deciderà come renderli.

Invece di far eseguire il rendering di una singola istanza Tile su un singolo oggetto di gioco, è possibile che una singola istanza Tile esegua il rendering di qualcosa che assomigli a una pila di oggetti. Questo sarebbe più efficiente e ti darebbe una netta separazione tra la tua logica del codice principale e come appare.

Proprio come i muri. La logica è molto banale, ma il rendering è più complesso.


3
Non vedo come MVC (che non è comune nei giochi della mia esperienza) possa aiutare qui. Semmai, la cosa peggiorerebbe: finirebbe con una ridondanza tra il modello e la vista che deve essere mantenuta sincronizzata.
munifico

1
Sì, quando hai un problema, usa <buzzword>. Ora hai due problemi.
Nevermind

1
np, ho scaricato più dettagli.
ashes999

OK ho ripreso il mio voto negativo. Tuttavia, non credo che MVC sia utile qui. La maggior parte delle volte, l'oggetto differisce per logica, non (solo) per rappresentazione grafica.
Nevermind

Non posso piacere a tutti, immagino. Per me, MVC è immediatamente saltato fuori come parte della soluzione a parte del problema. Ma sono d'accordo sul fatto che non sto fornendo una soluzione completa a tutti i problemi - ciò richiederebbe un saggio!
ashes999,

0

Sono d'accordo con @Omnion. Utilizzare l'approccio elenco, ma mantenerlo ordinato se le prestazioni sono un problema. Vale a dire usare un approccio ibrido. Utilizzare l'elenco come terza dimensione, ma identificare solo i primi 5 elementi come un tipo specifico e qualsiasi cosa successiva sia un tipo di inchiostro o un tipo che non richiederà il controllo più volte per fotogramma.


0

Non dovresti "mai" creare classi separate per cose come le tessere. Dovresti creare una struttura da byte, short e ints che si riferiscono al tipo di piastrella e oggetti su di essa. Gli saggi prestazionali sono i migliori con o anche a lungo su sistemi a 64 bit. Ma se mai vuoi salvare la tua mappa, dovresti andare con byte o cortometraggi ovunque tu sia, specialmente per mappe enormi.

Nei miei giochi ho una classe di mappe che ha solo campi come: byte tileType; 256 diversi tipi di terreno sono sufficienti per questo gioco. Richiede 1 byte. ushort tileObject; 65.536 oggetti diversi sono molti da abbinare. Richiede 2 byte ecc.

Se prendo l'esempio sopra ogni piastrella prende solo 3 byte in memoria. Quindi un 10000x10000 avrebbe una memoria di 300 MB e non dovrebbe essere un problema. Ogni volta che è necessario cercare qualcosa, si determina dove sulla mappa si desidera cercare e cosa si sta cercando e si esegue l'iterazione degli array di conseguenza.

Se hai roba dinamica su tutta la mappa, chiediti se il giocatore nota davvero quella roba lontano dalla sua finestra.

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.