Pregunta

¿Cómo puedo tener esa funcionalidad en mi juego a través de la cual los jugadores pueden cambiar su peinado, apariencia, estilo de ropa, etc., y así, cada vez que usan una prenda de vestir diferente, su avatar se actualiza con él?

Debería:

  • Haz que mi diseñador cree todas las combinaciones posibles de armaduras, peinados y caras como sprites (esto podría ser mucho trabajo).

  • Cuando el jugador elige el aspecto que deben tener durante su introducción al juego, mi código creará automáticamente este sprite, y todas las combinaciones posibles de sombreros / armaduras con ese sprite. Luego, cada vez que seleccionan una armadura diferente, se carga el sprite para esa combinación de armadura / look.

  • ¿Es posible tener un sprite de personaje dividido en componentes, como cara, camisa, jeans, zapatos, y tener las dimensiones en píxeles de cada uno de estos. Luego, cuando el jugador cambia su casco, por ejemplo, usamos las dimensiones en píxeles para colocar la imagen del casco en lugar de donde normalmente estaría la imagen de la cara. (Estoy usando Java para construir este juego)

  • ¿No es esto posible en 2D y debería usar 3D para esto?

  • ¿Algún otro método?

Por favor avise.

¿Fue útil?

Solución

3D no será necesario para esto, pero el algoritmo de pintor que es común en el mundo 3D podría, en mi opinión, ahorrarle algo de trabajo:

El algoritmo del pintor funciona dibujando primero los objetos más distantes, luego sobregirando con objetos más cerca de la cámara. En su caso, se reduciría a generar el búfer para su sprite, dibujándolo en el búfer, encontrando la siguiente parte de sprite dependiente (es decir, armadura o lo que sea), dibujando eso, encontrando la siguiente parte de sprite dependiente (es decir, una especial signo que está en la armadura), y así sucesivamente. Cuando ya no hay partes dependientes, pinta el sprite generado completo en la pantalla que ve el usuario.

Las partes combinadas deben tener un canal alfa (RGBA en lugar de RGB) para que solo combine las partes que tienen un valor alfa establecido en un valor de su elección. Si no puede hacerlo por el motivo que sea, simplemente siga con una combinación de RGB que tratará como transparente.

El uso de 3D puede hacer que la combinación de las partes sea más fácil para usted, y ni siquiera tendría que usar un búfer fuera de pantalla o escribir el código de combinación de píxeles. La otra cara es que necesitas aprender un poco de 3D si aún no lo sabes. :-)

Editar para responder a un comentario:

La parte de la combinación funcionaría de esta manera (en C ++, Java será bastante similar; ten en cuenta que no ejecuté el código siguiente a través de un compilador):

// 
// @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];
              }               
        }
     }
}

Para responder a su pregunta para una solución 3D, simplemente dibujaría rectángulos con sus respectivas texturas (que tendrían un canal alfa) uno sobre el otro. Debería configurar el sistema para que se muestre en modo ortogonal (para OpenGL: gluOrtho2D () ).

Otros consejos

Un factor importante a considerar es la animación. Si un personaje tiene una armadura con hombreras, esas hombreras pueden necesitar moverse con su torso. Del mismo modo, si lleva botas, esos tienen que seguir los mismos ciclos que harían los pies descalzos ocultos.

Básicamente, lo que necesitas para tus diseñadores es un Sprite Sheet que le permite a tus artistas ver todos los cuadros de animación posibles para tu personaje base. Luego, pídales que creen peinados, botas, armaduras, etc. personalizados basados ??en esas sábanas. Sí, es mucho trabajo, pero en la mayoría de los casos, los elementos requerirán una cantidad mínima de redibujado; Las botas son lo único que pude ver que realmente requiere mucho trabajo para volver a crearlas, ya que cambian en varios cuadros de animación. Sé despiadado con tus sprites, intenta reducir el número requerido tanto como sea posible.

Después de haber acumulado una biblioteca de elementos, puedes comenzar a hacer trampa. Recicla el mismo peinado y ajusta su color ya sea en Photoshop o directamente en el juego con controles deslizantes en tu creador de personajes.

El último paso, para asegurar un buen rendimiento en el juego, sería aplanar las hojas de sprite de los diferentes elementos en una sola hoja de sprite que luego se divide y almacena en buffers de sprite.

Iría con la solución generación de procedimientos (# 2). Mientras no haya una cantidad limitada de sprites para ser generados, tal que la generación demore demasiado. Tal vez haga la generación cuando se adquiere cada artículo, para disminuir la carga.

Desde que me pidieron en los comentarios que proporcionara un modo 3D también, aquí hay algunos, es un extracto de un código que escribí hace bastante tiempo. Es OpenGL y C ++.

A cada sprite se le pedirá que se dibuje. Usando el patrón Adaptador, combinaría sprites, es decir, habría sprites que sostendrían dos o más sprites que tenían una posición relativa (0,0) y un sprite con una posición real que tuviera todos esos sprites de sub-quot;

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();
}

El tex_id_ es un valor integral que identifica qué textura se utiliza para OpenGL. Las partes relevantes del gestor de texturas son estas. El administrador de texturas en realidad emula un canal alfa al verificar si el color leído es blanco puro (RGB de (ff, ff, ff)): el código loadFile opera en archivos BMP de 24 bits por píxel:

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;          
}

En realidad, fue un pequeño Jump'nRun que desarrollé ocasionalmente en mi tiempo libre. Utilizó el modo gluOrtho2D () también, por cierto. Si deja significa ponerse en contacto con usted, le enviaré la fuente si lo desea.

Los juegos 2d más antiguos como Diablo y Ultima Online utilizan una técnica de composición de sprites para hacer esto. Puedes buscar arte de ese tipo de juegos isométricos 2d más antiguos para ver cómo lo hicieron.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top