Modo efficiente di disegnare contorni intorno agli sprite


21

Sto usando XNA per programmare un gioco e sto sperimentando vari modi per ottenere un effetto "selezionato" sui miei sprite. Il problema che sto riscontrando è che ogni clic cliccabile nello spritebatch viene disegnato usando più di un singolo sprite (ogni oggetto può essere composto da un massimo di 6 sprite).

Gradirei se qualcuno mi potesse consigliare su come potrei ottenere l'aggiunta di un contorno ai miei sprite di pixel X (quindi la larghezza del contorno può essere vari numeri di pixel interi).

Grazie in anticipo,

  • Greg.

Risposte:


20

Di gran lunga il modo più semplice per farlo (quindi probabilmente il modo migliore, a meno che tu non sia davvero a corto di prestazioni) è avere due copie dei tuoi sprite.

  • La versione normale
  • Una versione "grassa" e incolore - fondamentalmente una versione bianca del tuo sprite X molti pixel "più grassi" rispetto all'originale.

Disegna l'intero oggetto usando la versione "fat", quindi disegna la versione normale sopra.

Rendendo la versione "grassa" bianca, è possibile utilizzare la colorazione integrata di SpriteBatch per modificare dinamicamente il colore di selezione.

Per generare la tua versione "grassa", ti consiglio di scrivere un'estensione della pipeline di contenuti in grado di prendere automaticamente i tuoi sprite originali, leggere il loro canale alfa, creare un nuovo canale alfa campionando il canale alfa massimo nell'immagine originale X-molti pixel attorno a ciascun pixel, e impostazione RGB = (1,1,1).

Dovrai assicurarti che tutti gli sprite abbiano un bordo trasparente sufficiente per aggiungere il contorno (puoi verificarlo nel processore dei contenuti e persino fare spazio se necessario).

Se hai solo alcuni sprite, puoi semplicemente usare un buon editor di immagini (GIMP, Photoshop) e farlo a mano: canale alfa per la selezione, espandi la selezione, selezione in alfa, riempi i canali di colore bianco.


4

Immagino che devi disegnare tutti i pezzi che ogni oggetto per primo in un singolo sprite. Quindi penso che dovresti scrivere uno shader per rilevare i bordi dello sprite e disegnare un pixel ovunque trovi un bordo. Mi aspetto che ci siano già degli shader là fuori per farlo, che potresti usare o port.


11
Questo tipo di shader è sorprendentemente fastidioso da scrivere, l'ho sentito (l'abbiamo usato in uno dei nostri giochi 3D e abbiamo continuato a imbatterci in casi sgradevoli). Una cosa che potresti prendere in considerazione è la codifica del contorno direttamente nella trama dello sprite con un colore specifico come (0, 255, 255, 0) e basta che lo shader trasformi quel colore quando lo sprite è selezionato. Quindi lo shader è un banale cambio di colore e hai un alto livello di controllo dell'artista sul contorno e su tutti i dettagli che desideri.

4

A seconda dei requisiti, ciò che potrebbe anche essere efficace è semplicemente la creazione di uno schema su richiesta per lo sprite. Suppongo che i tuoi sprite abbiano trasparenza e siano di forma irregolare anziché essere solo rettangoli (mentre per questo funzionerebbe bene, i rettangoli di contorno dovrebbero essere Trivial).

when selected:
   outline = new sprite canvas of appropriate size
   for sprite in object:
      # use larger numbers for thicker outlines
      for x in (-1, 0, 1) and y in (-1, 0, 1):
         render sprite mask into canvas at x,y with desired color

Nota che non è necessario farlo ogni volta che disegni (anche se suppongo che potresti), ma crea semplicemente il nuovo sprite di contorno quando cambi gli sprite.


Sì, questo è quello che volevo anche suggerire. Rendering dello sprite mascherato 1 pixel a sinistra, a destra, in alto e in basso dello sprite originale, sotto lo sprite esistente. Molti giochi hanno utilizzato questo metodo per delineare il testo renderizzato o con solo 2 delle 4 posizioni per creare un'ombra discendente.
Kaj,

1

L'approccio della forza bruta più semplice è quello di costruire due copie di ogni sprite, una normale e una evidenziata. Quindi scambiali quando evidenziati.

Se hai memoria da risparmiare, non è necessario complicarsi. Inoltre, gli artisti hanno il controllo completo sull'aspetto quando evidenziato, in modo da poter fare un contorno o qualsiasi altra cosa tu voglia.


Penso che parte del problema sia che l'OP afferma che ogni oggetto può essere composto da più sprite. Quindi immagino che il contorno debba essere il contorno dell'oggetto combinato, invece di avere un contorno separato attorno a ogni sprite del componente.
Chris Howe

1
Chris, non deve essere il contorno dell'oggetto combinato e funzionerà bene per un oggetto composto da più sprite. Fai esattamente quello che ha detto wkerslake, ma assicurati di disegnare gli sprite evidenziati dietro tutti gli sprite normali per quell'oggetto. In questo modo, indipendentemente dal numero di sprite di cui è composto un oggetto, l'evidenziazione utilizzerà solo leggermente più del doppio del tempo di disegno per quell'oggetto, che dovrebbe essere molto inferiore rispetto alla generazione dei contorni in tempo di ritorno con gli sprite combinati.
Attaccando

1

Che ne dici di ogni sprite, hai anche un altro sprite che è un contorno dello sprite di base. Quando si disegna un oggetto delineato, disegnare gli sprite di base, quindi creare una maschera del rendering combinato, quindi disegnare gli sprite di contorno escludendo la maschera.


2
Perché non disegnare prima gli sprite di contorno e disegnare gli sprite regolari su di essi?
Chris Howe,

Un motivo per non farlo (o il suggerimento di Chris) è perché usa il doppio della memoria di trama; un altro motivo è che il flusso di lavoro dell'artista è una schifezza perché è necessario aggiornare due file ogni volta che si cambia uno sprite.

2
Beh sì, idealmente non avresti due risorse di sprite reali. Se stai usando il test alfa per i tuoi sprite potresti mettere il contorno nello sprite e dargli un alfa leggermente più alto delle tue aree trasparenti. Quindi controllando il valore di riferimento del test alfa è possibile controllare se il contorno è visibile o meno. Tutto quello che stavo dicendo è che se hai 2 versioni degli sprite (che siano i suoi 2 asset o 2 stati dello stesso asset) dovresti prima disegnare la versione di contorno e poi la versione normale. In questo modo non hai bisogno di shader, maschere o altro.
Chris Howe,

1
L'idea di Chris ha merito; inoltre è possibile evitare che l'artista aggiorni 2 file (o anche la stessa risorsa) se si produce uno strumento per generare l'alfa per gli sprite di contorno eseguiti come parte della pipeline di risorse / contenuti. La memoria delle trame può essere o meno un problema, ma gli sprite di contorno separati dovrebbero essere altamente comprimibili DXT; possibilmente 1/6 della dimensione della trama originale nella memoria video e senza perdita di fedeltà.
jpaver,

1

Alcune soluzioni diverse con diversi compromessi.

Più semplice: Rendi l'oggetto usando un colore piatto più volte e inclina la posizione (offset a sinistra, su, giù, a destra ecc.), Questo creerà una versione di contorno di tutto ciò che visualizzi sopra di esso, ma ha costi di prestazione e non lo farà consentire bordi grassi senza molti rendering extra. Un bordo di uno o due pixel può andare bene con 4x.

Il più veloce: preelaborare la trama e avere una copia che è già delimitata, o è solo il bordo, oppure è una maschera piatta in scala di grigi a 8 bit che è possibile colorare in uno shader. Questo sarà probabilmente veloce a costo della memoria.

Migliore: la mia opinione, ma generare una rappresentazione Signed Distance Field (SDF) del tuo oggetto sarebbe probabilmente la soluzione migliore. Queste trame possono essere molto più piccole della trama di origine e catturare comunque dati utili. In sostanza ogni pixel codifica quanto è lontano dall'oggetto utilizzato per generarlo. Con questi dati in mano, puoi scrivere tutti i tipi di effetti dai bagliori ai contorni. Il bordo potrebbe cambiare in dimensioni, colore ecc., Ed è ancora uno shader relativamente economico e solo un disegno in più. L'aspetto negativo è la strumentazione e la preelaborazione.


0

Non sono sicuro dell'efficienza, ma il modo più semplice che posso vedere sarebbe quello di disegnare una versione più grande dello sprite nel colore che si desidera selezionare per primo. Disegna lo sprite sopra di quello. Vedrai solo il bordo del primo sprite, dando l'effetto della selezione.

EDIT: Tuttavia, come puoi vedere dai commenti, questa non è una buona idea.


1
Il solo ridimensionamento di uno sprite non fornisce un vero schema
Iain,

2
Non penso che sarebbe, ma sarebbe facile.
Il comunista Duck il

2
Molte cose sono facili ma non risolvono il problema. Per gli sprite con qualsiasi tipo di silhouette interessante un ingrandimento produrrà immondizia assoluta.

Il ridimensionamento avrà un bell'aspetto solo per oggetti solidi quadrati o circolari. Se la forma è davvero larga, o alta, o presenta degli spazi vuoti, sembrerà davvero brutta.
Attaccando

3
Il ridimensionamento darà risultati migliori rispetto al non fare nulla e sarebbe anche un utile passo nel cammino verso la "perfezione". Mentre i risultati grafici non saranno perfetti, se questo fosse per uno strumento interno potrebbe essere abbastanza buono.
dash-tom-bang,

0

Sono d'accordo con il ridimensionamento dello sprite. Di gran lunga il percorso più semplice, e puoi applicarlo alla selezione di QUALSIASI sprite senza dover creare sprite aggiuntive appositamente per questo scopo.

  • cioè spriteOutline.Scale = spriteOriginal.Scale * 0.1f; spriteOutline.Color = new Color (255, 0, 0, 255);

0

Sostituisci il colore dello sprite originale con il colore del contorno (o anche coloralo se vuoi). Rendering di questo sprite ombreggiato o colorato quattro volte con un offset di 1 pixel: a x, y = (- 1, -1), quindi (+ 1, -1), quindi (-1, + 1) quindi (+1 , +1). Ripetere l'operazione per tutti gli sprite che compongono l'oggetto.

Successivamente, esegui il rendering degli sprite originali nell'ordine corretto in cima a (0,0).

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.