Domanda

Come posso avere quella funzionalità nel mio gioco attraverso la quale i giocatori possono cambiare la loro pettinatura, aspetto, stile di abbigliamento, ecc. E così ogni volta che indossano un capo diverso il loro avatar viene aggiornato con esso.

Dovrei:

  • Chiedi al mio designer di creare tutte le possibili combinazioni di armature, acconciature e facce come folletti (questo potrebbe richiedere molto lavoro).

  • Quando il giocatore sceglie come dovrebbe apparire durante la sua introduzione al gioco, il mio codice creerebbe automaticamente questo sprite e tutte le possibili combinazioni di copricapo / armatura con quello sprite. Quindi ogni volta che selezionano armature diverse, viene caricato lo sprite per quella combinazione armatura / aspetto.

  • È possibile avere lo sprite di un personaggio diviso in componenti, come viso, camicia, jeans, scarpe, e avere le dimensioni in pixel di ciascuno di questi. Quindi, ogni volta che il giocatore cambia l'elmetto, ad esempio, utilizziamo le dimensioni in pixel per posizionare l'immagine dell'elmetto al posto di dove sarebbe normalmente l'immagine del viso. (Sto usando Java per creare questo gioco)

  • Non è possibile in 2D e per questo dovrei usare il 3D?

  • Qualunque altro metodo?

Si prega di avvisare.

È stato utile?

Soluzione

Il 3D non sarà necessario per questo, ma l'algoritmo del pittore che è comune nel mondo 3D potrebbe IMHO farti risparmiare un po 'di lavoro:

L'algoritmo del pittore funziona disegnando prima gli oggetti più distanti, quindi eseguendo il superamento con gli oggetti più vicini alla fotocamera. Nel tuo caso, si ridurrebbe a generare il buffer per il tuo sprite, disegnandolo sul buffer, trovando la successiva parte di sprite dipendente (cioè armatura o quant'altro), disegnandola, trovando la successiva parte di sprite dipendente (cioè uno speciale segno che è sull'armatura) e così via. Quando non ci sono più parti dipendenti, dipingi lo sprite completamente generato sul display che l'utente vede.

Le parti combinate devono avere un canale alfa (RGBA anziché RGB) in modo da combinare solo le parti con un valore alfa impostato su un valore a scelta. Se non riesci a farlo per qualsiasi motivo, mantieni una combinazione RGB che considererai trasparente.

L'uso del 3D potrebbe semplificare la combinazione delle parti e non dovresti nemmeno utilizzare un buffer fuori schermo o scrivere il codice di combinazione dei pixel. Il rovescio della medaglia è che devi imparare un po 'di 3D se non lo conosci già. :-)

Modifica per rispondere al commento:

La parte della combinazione funzionerebbe in qualche modo in questo modo (in C ++, Java sarà abbastanza simile - tieni presente che non ho eseguito il codice seguente tramite un compilatore):

// 
// @param dependant_textures is a vector of textures where 
// texture n+1 depends on texture n. 
// @param combimed_tex is the output of all textures combined
void Sprite::combineTextures (vector<Texture> const& dependant_textures, 
                              Texture& combined_tex) {
   vector< Texture >::iterator iter = dependant_textures.begin();
   combined_tex = *iter;

   if (dependant_textures.size() > 1)
     for (iter++; iter != dependant_textures.end(); iter++) {
        Texture& current_tex = *iter;

        // Go through each pixel, painting:
        for (unsigned char pixel_index = 0; 
             pixel_index < current_tex.numPixels(); pixel_index++) {
           // Assuming that Texture had a method to export the raw pixel data
           // as an array of chars - to illustrate, check Alpha value:
           int const BYTESPERPIXEL = 4; // RGBA
           if (!current_tex.getRawData()[pixel_index * BYTESPERPIXEL + 3]) 
              for (int copied_bytes = 0; copied_bytes < 3; copied_bytes++)
              {
                int index = pixel_index * BYTESPERPIXEL + copied_bytes;
                combined_tex.getRawData()[index] = 
                   current_tex.getRawData()[index];
              }               
        }
     }
}

Per rispondere alla tua domanda di soluzione 3D, devi semplicemente disegnare rettangoli con le rispettive trame (che avrebbero un canale alfa) l'uno sull'altro. Impostare il sistema in modo che venga visualizzato in modalità ortogonale (per OpenGL: gluOrtho2D () ).

Altri suggerimenti

Uno dei principali fattori da considerare è l'animazione. Se un personaggio ha un'armatura con spalline, potrebbe essere necessario muoverle con il busto. Allo stesso modo, se indossa stivali, quelli devono seguire gli stessi cicli dei piedi nudi nascosti.

In sostanza ciò di cui hai bisogno per i tuoi designer è un Sprite Sheet che consente ai tuoi artisti di vedere tutti i possibili frame di animazione per il tuo personaggio base. Poi li fai creare acconciature personalizzate, stivali, armature, ecc. Basati su quei fogli. Sì, è un sacco di lavoro, ma nella maggior parte dei casi, gli elementi richiedono una quantità minima di ridisegno; gli stivali sono l'unica cosa che potrei vedere impiegare molto lavoro per ricreare poiché cambiano su più fotogrammi di animazione. Sii spietato con i tuoi sprite, cerca di ridurre il numero richiesto il più possibile.

Dopo aver accumulato una libreria di elementi puoi iniziare a imbrogliare. Ricicla lo stesso stile di capelli e regola il suo colore in Photoshop o direttamente nel gioco con i cursori nel creatore del tuo personaggio.

L'ultimo passo, per garantire buone prestazioni nel gioco, sarebbe quello di appiattire tutti i diversi fogli degli sprite degli elementi in un singolo foglio sprite che viene poi suddiviso e memorizzato nei buffer degli sprite.

Vado con la generazione procedurale (# 2). Finché non vi è una quantità limitante di sprite da generare, tale che la generazione impiega troppo tempo. Forse fare la generazione quando viene acquisito ogni articolo, per ridurre il carico.

Dato che nei commenti mi è stato chiesto di fornire anche un modo 3D, eccone alcuni, che è un estratto di un codice che ho scritto qualche tempo fa. Sono OpenGL e C ++.

Ad ogni sprite verrebbe chiesto di disegnare se stesso. Usando il modello Adapter, vorrei combinare gli sprite - cioè ci sarebbero sprite che avrebbero tenuto due o più sprite che avevano una posizione relativa (0,0) e uno sprite con una posizione reale che aveva tutti quegli sprite "secondari".

void Sprite::display (void) const
{
  glBindTexture(GL_TEXTURE_2D, tex_id_);
  Display::drawTranspRect(model_->getPosition().x + draw_dimensions_[0] / 2.0f,
      model_->getPosition().y + draw_dimensions_[1] / 2.0f,
      draw_dimensions_[0] / 2.0f, draw_dimensions_[1] / 2.0f);
}

void Display::drawTranspRect (float x, float y, float x_len, float y_len)
{   
  glPushMatrix();

  glEnable(GL_BLEND);   
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glColor4f(1.0, 1.0, 1.0, 1.0);

  glBegin(GL_QUADS);        
    glTexCoord2f(0.0f, 0.0f); glVertex3f(x - x_len, y - y_len, Z);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(x + x_len, y - y_len, Z);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(x + x_len, y + y_len, Z);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(x - x_len, y + y_len, Z);
  glEnd();

  glDisable(GL_BLEND);  
  glPopMatrix();
}

Il tex_id_ è un valore integrale che identifica la trama utilizzata per OpenGL. Le parti rilevanti del gestore delle trame sono queste. Il gestore delle texture emula effettivamente un canale alfa controllando se il colore letto è bianco puro (RGB di (ff, ff, ff)) - il codice loadFile funziona su file BMP a 24 bit per pixel:

TextureManager::texture_id 
TextureManager::createNewTexture (Texture const& tex) {
    texture_id id;
    glGenTextures(1, &id);
    glBindTexture(GL_TEXTURE_2D, id);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);   
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);       
    glTexImage2D(GL_TEXTURE_2D, 0, 4, tex.width_, tex.height_, 0, 
        GL_BGRA_EXT, GL_UNSIGNED_BYTE, tex.texture_);

    return id;
}

void TextureManager::loadImage (FILE* f, Texture& dest) const {
  fseek(f, 18, SEEK_SET);
  signed int compression_method;
  unsigned int const HEADER_SIZE = 54;

  fread(&dest.width_, sizeof(unsigned int), 1, f);
  fread(&dest.height_, sizeof(unsigned int), 1, f);
  fseek(f, 28, SEEK_SET);
  fread(&dest.bpp_, sizeof (unsigned short), 1, f);
  fseek(f, 30, SEEK_SET);
  fread(&compression_method, sizeof(unsigned int), 1, f);

  // We add 4 channels, because we will manually set an alpha channel
  // for the color white.
  dest.size_ = dest.width_ * dest.height_ * dest.bpp_/8 * 4;
  dest.texture_ = new unsigned char[dest.size_];
  unsigned char* buffer = new unsigned char[3 * dest.size_ / 4];    

  // Slurp in whole file and replace all white colors with green
  // values and an alpha value of 0:
  fseek(f, HEADER_SIZE, SEEK_SET);      
  fread (buffer, sizeof(unsigned char), 3 * dest.size_ / 4, f); 
  for (unsigned int count = 0; count < dest.width_ * dest.height_; count++) {       
    dest.texture_[0+count*4] = buffer[0+count*3];
    dest.texture_[1+count*4] = buffer[1+count*3];
    dest.texture_[2+count*4] = buffer[2+count*3];
    dest.texture_[3+count*4] = 0xff;

    if (dest.texture_[0+count*4] == 0xff &&
        dest.texture_[1+count*4] == 0xff &&
        dest.texture_[2+count*4] == 0xff) {
      dest.texture_[0+count*4] = 0x00;
      dest.texture_[1+count*4] = 0xff;
      dest.texture_[2+count*4] = 0x00;
      dest.texture_[3+count*4] = 0x00;
      dest.uses_alpha_ = true;
    }                   
  }
  delete[] buffer;          
}

Questo era in realtà un piccolo Jump'nRun che ho sviluppato occasionalmente nel mio tempo libero. Ha usato anche la modalità gluOrtho2D (), a proposito. Se parti significa contattarti, ti invierò la fonte se lo desideri.

I vecchi giochi 2D come Diablo e Ultima Online usano una tecnica di composizione dello sprite per fare questo. Puoi cercare arte da quel tipo di vecchi giochi isometrici 2D per vedere come hanno fatto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top