Come gestire immagini arbitrariamente grandi nei giochi 2D?


13

Domanda

Quando hai un gioco 2D che utilizza immagini molto grandi (più grandi di quelle che il tuo hardware può supportare), cosa fai? Forse c'è qualche soluzione interessante a questo problema che non mi è mai successo prima.

In pratica sto lavorando a una sorta di creatore di giochi di avventura grafica. Il mio target di riferimento sono persone con poca o nessuna esperienza di sviluppo (e programmazione) di giochi. Se stessi lavorando con un'applicazione del genere, non preferiresti che gestisse il problema internamente piuttosto che dirti di "dividere le tue immagini"?

Contesto

È noto che le carte grafiche impongono una serie di restrizioni sulla dimensione delle trame che possono usare. Ad esempio, quando lavori con XNA sei limitato a una dimensione massima della trama di 2048 o 4096 pixel a seconda del profilo che scegli.

Di solito questo non rappresenta un grosso problema nei giochi 2D perché la maggior parte di essi ha livelli che possono essere costruiti da singoli pezzi più piccoli (ad esempio tessere o folletti trasformati).

Ma pensa ad alcuni classici giochi di avventura grafica point'n'click (ad esempio Monkey Island). Le stanze nei giochi di avventura grafica sono generalmente progettate nel loro insieme, con sezioni poco o niente ripetibili, proprio come un pittore che lavora su un paesaggio. O forse un gioco come Final Fantasy 7 che utilizza grandi sfondi pre-renderizzati.

Alcune di queste stanze possono diventare piuttosto grandi, spesso occupando tre o più schermi di larghezza. Se consideriamo questi sfondi nel contesto di un moderno gioco ad alta definizione, supereranno facilmente le dimensioni massime della trama supportate.

Ora, la soluzione ovvia a questo è quella di dividere lo sfondo in sezioni più piccole che si adattano ai requisiti e disegnarli fianco a fianco. Ma tu (o il tuo artista) dovresti essere tu a dividere tutte le tue trame?

Se stai lavorando con un'API di alto livello, non dovrebbe forse essere preparato per affrontare questa situazione e fare la divisione e l'assemblaggio delle trame internamente (o come pre-processo come Content Pipeline di XNA o in fase di esecuzione)?

modificare

Ecco cosa stavo pensando, dal punto di vista dell'implementazione di XNA.

Il mio motore 2D richiede solo un piccolo sottoinsieme della funzionalità di Texture2D. Posso effettivamente ridurlo a questa interfaccia:

public interface ITexture
{
    int Height { get; }
    int Width { get; }
    void Draw(SpriteBatch spriteBatch, Vector2 position, Rectangle? source, Color  color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
}

L'unico metodo esterno che utilizzo che si basa su Texture2D è SpriteBatch.Draw () in modo da poter creare un ponte tra di loro aggiungendo un metodo di estensione:

    public static void Draw(this SpriteBatch spriteBatch, ITexture texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
    {
        texture.Draw(spriteBatch, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
    }

Ciò mi consentirebbe di utilizzare un ITexture in modo intercambiabile ogni volta che ho usato un Texture2D prima.

Quindi potrei creare due diverse implementazioni di ITexture, ad esempio RegularTexture e LargeTexture, in cui RegularTexture semplicemente avvolgerebbe un normale Texture2D.

D'altra parte, LargeTexture manterrebbe un Elenco di Texture2D ciascuno corrispondente a uno dei pezzi divisi dell'immagine completa. La parte più importante è che la chiamata a Draw () su LargeTexture dovrebbe essere in grado di applicare tutte le trasformazioni e disegnare tutti i pezzi come se fossero solo un'immagine contigua.

Infine, per rendere trasparente l'intero processo, creerei una classe di caricatore di texture unificata che fungerebbe da metodo Factory. Prenderebbe il percorso della trama come parametro e restituirebbe una ITexture che sarebbe una RegularTexture o una LargeTexture a seconda della dimensione dell'immagine che supera o meno il limite. Ma l'utente non aveva bisogno di sapere quale fosse.

Un po 'eccessivo o suona bene?


1
Bene, se vuoi andare con la divisione dell'immagine, estendi la pipeline di contenuti per farlo programmaticamente. Non tutte le funzionalità esistono per coprire ogni singolo scenario e ad un certo punto dovrai estenderlo da solo. La suddivisione personale suona come la soluzione più semplice, in quanto è quindi possibile gestire le immagini in modo simile a mappe a tessere, su cui ci sono numerose risorse.
DMan,

Farlo sulla pipeline di contenuti è l'approccio più sensato. Ma nel mio caso avevo bisogno di caricare le mie immagini in fase di runtime e sono già riuscito a trovare una soluzione di runtime (motivo per cui ho fatto questa domanda ieri). Ma la vera bellezza deriva dal prendere questa "mappa delle piastrelle" e farla comportare come una normale trama. Cioè puoi ancora passarlo a spritebatch per disegnare, e puoi ancora specificare posizione, rotazione, scala, rettangoli src / dest, ecc. Sono a circa metà strada ormai ormai :)
David Gouveia,

A dire il vero, sono già al punto in cui mi chiedo se tutto ciò che è veramente valsa la pena o se dovrei semplicemente mostrare un avviso all'utente che gli dice che la dimensione massima supportata dell'immagine è 2048x2048 e che si debba fare.
David Gouveia, il

Dipende davvero da te per il design. Se finisci il progetto e non riesci a scorrere tra le stanze, sarà meglio un editor non finito che ti consenta di scorrere trame di grandi dimensioni: D
Aleks

Nasconderlo nell'API sembra un bel modo per rendere più facile la vita degli autori di giochi. Probabilmente lo implementerei nelle classi Polygon (sprite) e Texture, e non nel caso speciale delle immagini più piccole, ma piuttosto le trasformerei in una tessera 1x1. Quindi la classe di trama è un gruppo di trama che descrive tutte le tessere e quante righe / colonne. La classe Polygon quindi osserva queste informazioni per suddividersi e disegnare ogni riquadro con la trama appropriata. In questo modo, è la stessa classe. Punti bonus se la tua classe sprite disegna solo riquadri visibili e i gruppi di trame non caricano trame fino a quando necessario.
uliwitness

Risposte:


5

Penso che tu sia sulla buona strada. Devi dividere le immagini in pezzi più piccoli. Da quando crei un creatore di giochi non puoi utilizzare la pipeline di contenuti di XNA. A meno che il produttore non assomigli di più a un motore che richiederà comunque agli sviluppatori di utilizzare Visual Studio. Quindi devi costruire le tue routine che faranno la scissione. Dovresti anche prendere in considerazione lo streaming dei pezzi un po 'prima che diventino visibili in modo da non limitare la quantità di memoria video che i giocatori dovrebbero avere.

Ci sono alcuni trucchi che puoi fare appositamente per i giochi di avventura. Immagino che come tutti i classici giochi di avventura avrai pochi strati di queste trame in modo che ... il tuo personaggio possa camminare dietro l'albero o l'edificio ...

Se vuoi che questi strati abbiano l'effetto paralassante, quando dividi le tue tessere salta tutto il traslucido una volta. Qui devi pensare alla dimensione delle piastrelle. I riquadri più piccoli aggiungono la gestione delle spese generali e disegnano le chiamate, ma saranno meno i dati in cui i riquadri più grandi saranno più compatibili con la GPU ma avranno molta traslucenza.

Se non hai l'effetto paralaxing, invece di coprire l'intera scena con 2-4 immagini giganti con una profondità di 32 bit, puoi creare solo una profondità di 32 bit. E puoi avere uno strato di stencil o di profondità che sarà solo 8 bit per pixel.

So che sembra difficile gestire le risorse da parte di sviluppatori non di giochi, ma in realtà non è necessario. Ad esempio, se hai una strada con 3 alberi e il giocatore va dietro a 2 di loro e davanti al terzo albero e al resto dello sfondo ... Crea il layout della scena nel modo che preferisci nel tuo editor e poi in un costruendo un passo dal tuo creatore di giochi puoi cuocere queste immagini giganti (ben piccole tessere di una grande immagine).

Su come i giochi classici lo stavano facendo. Non sono sicuro che probabilmente abbiano fatto trucchi simili ma devi ricordare che al momento lo schermo era 320x240 i giochi erano con 16 colori che quando si utilizzano trame palatelizzate ti permettono di codificare 2 pixel in un singolo byte. Ma non è così che funzionano le architetture attuali: D

In bocca al lupo


Grazie, molte idee molto interessanti qui! In realtà ho visto la tecnica del livello di stencil usata prima in Adventure Game Studio. Per quanto riguarda la mia applicazione, sto facendo le cose in modo leggermente diverso. Le stanze non hanno necessariamente un'immagine di sfondo. Invece c'è una tavolozza di immagini che l'utente ha importato e un livello di sfondo / primo piano. L'utente è libero di trascinare e rilasciare i pezzi nella stanza per crearlo. Quindi, se lo desiderava, potrebbe comporlo anche in pezzi più piccoli senza dover creare oggetti di gioco non interattivi. Non c'è parallasse però, poiché ci sono solo quei due strati.
David Gouveia, il

E ho esaminato i file di dati per Monkey Island Special Edition (il remake HQ). Ogni sfondo è diviso esattamente in pezzi 1024x1024 (anche se gran parte dell'immagine non viene utilizzata). Ad ogni modo, controlla la mia modifica per capire cosa c'è in mente per rendere trasparente l'intero processo.
David Gouveia, il

Probabilmente sceglierei qualcosa di più ragionevole come 256x256. In questo modo non sprecherò risorse e se successivamente deciderò di implementare lo streaming caricherà blocchi più ragionevoli. Forse non l'ho spiegato abbastanza bene, ma ho suggerito che potresti cuocere tutti questi piccoli oggetti di gioco statici in una trama ampia. Ciò che salverai è, nel caso in cui tu abbia una trama di sfondo grande, tutti i pixel coperti dagli oggetti statici in alto.
Aleks,

Vedo, in modo da unire tutti gli oggetti statici in sottofondo, poi divisa e creare una texture atlante. Potrei anche cuocere tutto il resto nell'atlante delle trame seguendo quel treno di pensieri. Anche questa è una bella idea. Ma lo lascerò per dopo, perché in questo momento non ho alcun passaggio "build", ovvero sia l'editor che il client di gioco funzionano sullo stesso motore e sullo stesso formato di dati.
David Gouveia,

Mi piace l'approccio stencil ma se sto realizzando l'editor di giochi di avventura, probabilmente non lo userei ... Forse perché se sto costruendo un tale strumento, mi rivolgo agli sviluppatori di giochi. Bene ha i suoi pro e contro. Ad esempio, puoi usare un'altra immagine su cui i bit possono definire i trigger per pixel o se hai un po 'd'acqua animata puoi facilmente stencil. Nota: uno strano suggerimento non correlato esamina i diagrammi di stato / attività UML per i tuoi script.
Aleks,

2

Tagliali in modo che non siano più arbitrariamente grandi, come dici tu. Non c'è magia. Quei vecchi giochi 2D o l'hanno fatto o sono stati resi nel software, nel qual caso non c'erano restrizioni hardware oltre la dimensione della RAM. È da notare che molti di quei giochi 2D non avevano sfondi enormi, scorrevano lentamente e si sentivano enormi.

Quello che stai cercando di fare nel tuo creatore di giochi di avventura grafica è pre-elaborare quelle grandi immagini durante una sorta di fase di "costruzione" o "integrazione" prima che il gioco sia impacchettato per essere eseguito.

Se si desidera utilizzare un'API e una libreria esistenti invece di scriverne una propria, suggerirei di convertire quelle grandi immagini in riquadri durante quella "costruzione" e utilizzare un motore di riquadri 2D, di cui ce ne sono molti.

Se vuoi usare le tue tecniche 3D da solo, potresti comunque voler dividere in riquadri e utilizzare un'API 2D ben nota e ben progettata per scrivere la tua libreria 3D per questo, in questo modo sei sicuro di iniziare almeno con una buona progettazione API e meno progettazione richiesta da parte tua.


Farlo in fase di compilazione sarebbe l'ideale ... Ma il mio editor usa anche XNA per il rendering, non è solo il motore. Ciò significa che una volta che l'utente importa l'immagine e la trascina in una stanza, XNA dovrebbe già essere in grado di renderla. Quindi il mio grande dillema è se eseguire la divisione in memoria durante il caricamento del contenuto o su disco durante l'importazione del contenuto. Inoltre, suddividere fisicamente l'immagine in più file significherebbe che avrei bisogno di un modo per memorizzarli in modo diverso, mentre farlo in memoria in fase di esecuzione non richiederebbe alcuna modifica ai file di dati del gioco.
David Gouveia,

1

Se hai familiarità con l'aspetto di un quadrifoglio nella pratica visiva, ho usato un metodo che taglia immagini dettagliate di grandi dimensioni in immagini più piccole relativamente simili a come appare una prova / visualizzazione di un quadrifoglio. Tuttavia, i miei sono stati creati in questo modo per tagliare aree specifiche che potrebbero essere codificate o compresse in modo diverso in base al colore. È possibile utilizzare questo metodo e fare ciò che ho descritto, poiché è anche utile.

In fase di esecuzione il loro taglio richiederebbe temporaneamente più memoria e rallenterebbe i tempi di caricamento. Direi che dipende dal fatto che ti interessi di più all'archiviazione fisica o al tempo di caricamento di un gioco degli utenti realizzato con il tuo editor.

Tempo di caricamento / esecuzione: il modo in cui procederei per il caricamento consiste semplicemente nel tagliarlo in pezzi di dimensioni proporzionali. Se si tratta di un numero dispari di pixel, conta un pixel nella parte più a destra, ma a nessuno piace tagliare le loro immagini, in particolare gli utenti inesperti che potrebbero non sapere che non dipende interamente da loro. Renderli metà larghezza o metà altezza (o 3r, 4 ° qualunque) il motivo non in quadrati o 4 sezioni (2 righe 2 colonne) è perché ridurrebbe la memoria di piastrellatura poiché non ti preoccuperesti di xe y allo stesso tempo (e in base a quante immagini sono così grandi, potrebbe essere piuttosto importante)

Prima mano / Automaticamente: in questo caso mi piace l'idea di dare all'utente la scelta di memorizzarlo in un'immagine o di memorizzarlo come pezzi separati (un'immagine dovrebbe essere predefinita). Memorizzare l'immagine come .gif funzionerebbe alla grande. L'immagine sarebbe in un file, e piuttosto che ogni fotogramma fosse un'animazione sarebbe più simile a un archivio compresso di tutta la stessa immagine (che è essenzialmente ciò che è un .gif). Consentirebbe un facile trasporto del file fisico, facilità di caricamento:

  1. È un singolo file
  2. Tutti i file avrebbero una formattazione quasi identica
  3. Puoi facilmente scegliere quando AND se ritagliarlo in singole trame
  4. Potrebbe essere più semplice accedere alle singole immagini se sono già in un singolo file .gif caricato poiché non richiederebbe un gran casino di file di immagini in memoria

Oh, e se usi il metodo .gif, dovresti cambiare l'estensione del file in qualcos'altro (.gif -> .kittens) per meno confusione quando il tuo .gif si anima come se fosse un file rotto. (non preoccuparti della legalità, .gif è un formato aperto)


Ciao e grazie per il tuo commento! Scusate se ho frainteso ma ciò che avete scritto non riguarda principalmente la compressione e l'archiviazione? In questo caso, tutto ciò di cui mi preoccupo è che l'utente della mia applicazione sia in grado di importare qualsiasi immagine e che il motore (in XNA) sia in grado di caricare e visualizzare qualsiasi immagine in fase di esecuzione . Sono riuscito a risolverlo semplicemente usando GDI per leggere diverse sezioni dell'immagine in trame XNA separate e avvolgendole in una classe che si comporta proprio come una trama normale (cioè supporta il disegno e le trasformazioni nel loro insieme).
David Gouveia,

Ma per curiosità, potresti approfondire ulteriormente quali criteri hai usato per selezionare quale area dell'immagine è stata inserita in ogni sezione?
David Gouveia,

In parte potrebbe esserci un problema se hai meno esperienza con la gestione delle immagini. Ma essenzialmente, stavo descrivendo il taglio dell'immagine in segmenti. E sì, il punto .gif stava descrivendo sia l'archiviazione che il caricamento, ma è inteso come un metodo per tagliare l'immagine e quindi memorizzarla sul computer come è. Se lo salvi come gif, stavo dicendo che puoi tagliarlo nelle singole immagini e quindi salvarlo come gif animato, con ogni taglio dell'immagine salvato come frame di animazione separati. Funziona bene e ho anche aggiunto altre funzionalità con cui potrebbe essere d'aiuto.
Joshua Hedges,

Il sistema quadtree come ho descritto è interamente dichiarato come riferimento, in quanto è da dove ho avuto un'idea. Sebbene invece di affollare una domanda eventualmente utilizzabile per i lettori che potrebbero avere problemi simili, dovremmo usare la messaggistica privata se desideri saperne di più.
Joshua Hedges il

Roger, ho avuto la parte sull'uso dei frame GIF come un archivio di sorta. Ma mi chiedo anche, GIF non è un formato indicizzato con una tavolozza di colori limitata? Posso semplicemente memorizzare intere immagini RGBA a 32 bpp in ciascun fotogramma? E immagino che il processo di caricamento sia ancora più complicato, poiché XNA non supporta nulla di tutto ciò.
David Gouveia,

0

Sembrerà ovvio, ma perché non dividere la tua stanza in pezzi più piccoli? Scegli una dimensione arbitraria che non si scontrerà con le schede grafiche (come 1024x1024 o forse più grande) e dividi le tue stanze in più pezzi.

So che questo evita il problema e, onestamente, questo è un problema molto interessante da risolvere. Ad ogni modo, questa è una soluzione banale, che potrebbe funzionare un po 'di tempo per te.


Penso che avrei potuto menzionare il motivo da qualche parte in questo thread, ma poiché non sono sicuro che lo ripeterò. Fondamentalmente perché non è un gioco ma piuttosto un creatore di giochi per il grande pubblico (pensa a qualcosa sullo stesso ambito del creatore di giochi di ruolo), quindi invece di forzare la limitazione della dimensione delle trame quando gli utenti importano i propri beni, potrebbe essere bello da gestire la divisione e la cucitura automaticamente dietro le quinte senza mai disturbarle con i dettagli tecnici dei limiti delle dimensioni della trama.
David Gouveia,

Ok, ha senso. Scusa, immagino di essermi perso.
ashes999,
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.