Настраиваемый аватар игрока в 2D игре - PullRequest
3 голосов
/ 31 декабря 2008

Как я могу иметь такую ​​функциональность в моей игре, благодаря которой игроки могут менять свою прическу, внешний вид, стиль одежды и т. Д., И поэтому, когда они надевают другой предмет одежды, их аватар обновляется вместе с ним.

Должен ли я:

  • Пусть мой дизайнер создаст все возможные комбинации доспехов, причесок и лиц в качестве спрайтов (это может быть много работы).

  • Когда игрок выбирает, как он должен выглядеть во время своего знакомства с игрой, мой код автоматически создает этот спрайт и все возможные комбинации головных уборов / брони с этим спрайтом. Затем каждый раз, когда они выбирают какую-то другую броню, спрайт для этой комбинации броня / внешний вид загружается.

  • Можно ли разделить спрайт персонажа на такие компоненты, как лицо, рубашка, джинсы, обувь, и иметь размеры в пикселях каждого из них. Затем, всякий раз, когда игрок меняет свой шлем, например, мы используем размеры в пикселях, чтобы поместить изображение шлема на место, где обычно будет его изображение лица. (Я использую Java для создания этой игры)

  • Разве это невозможно в 2D, и я должен использовать 3D для этого?

  • Любой другой метод?

Пожалуйста, сообщите.

Ответы [ 5 ]

2 голосов
/ 31 декабря 2008

Одним из основных факторов, который следует учитывать, является анимация. Если у персонажа есть доспехи с наплечниками, эти наплечники, возможно, должны двигаться вместе с его торсом. Аналогично, если он носит ботинки, они должны следовать тем же циклам, что и спрятанные босые ноги.

По сути, для ваших дизайнеров вам нужен Sprite Sheet , который позволяет вашим художникам видеть все возможные кадры анимации для вашего основного персонажа. Затем вы можете создать собственные прически, ботинки, доспехи и т. Д. На основе этих листов. Да, это большая работа, но в большинстве случаев элементы требуют минимального количества перерисовки; Сапоги - это единственное, что я видел, что на самом деле требовалось много усилий для воссоздания, так как они переключаются на несколько кадров анимации. Тем не менее, будьте осторожны со своими спрайтами, постарайтесь максимально сократить необходимое количество.

После того, как вы накопили библиотеку элементов, вы можете начать обманывать. Переработайте ту же прическу и измените ее цвет в Photoshop или непосредственно в игре с помощью ползунков в создателе вашего персонажа.

Последний шаг, чтобы обеспечить хорошую производительность в игре, - это свести спрайт-листы всех элементов в один спрайт-лист, который затем разделяется и сохраняется в спрайтерских буферах.

1 голос
/ 31 декабря 2008

Я бы пошел с процедурным решением (# 2). До тех пор, пока не будет создано ограниченное количество спрайтов, так что генерация занимает слишком много времени. Может быть, сделать поколение, когда каждый элемент приобретен, чтобы снизить нагрузку.

1 голос
/ 31 декабря 2008

3D не понадобится для этого, но алгоритм рисования, распространенный в мире 3D, может ИМХО сэкономить вам немного работы:

Алгоритм рисования работает, сначала рисуя самые отдаленные объекты, а затем перерисовывая объекты ближе к камере. В вашем случае это будет сводиться к генерации буфера для вашего спрайта, его рисованию в буфере, поиску следующей зависимой части спрайта (т.е. брони или еще чего), рисованию этого, поиску следующей зависимой части спрайта (т.е. знак, что на доспехах) и тд. Когда больше нет зависимых частей, вы рисуете полностью сгенерированный спрайт на дисплее, который видит пользователь.

Комбинированные детали должны иметь альфа-канал (RGBA вместо RGB), так что вы будете комбинировать только те детали, для которых альфа-значение установлено на выбранное вами значение. Если вы не можете этого сделать по какой-либо причине, просто придерживайтесь одной комбинации RGB, которую вы будете считать прозрачной.

Использование 3D может упростить комбинирование частей, и вам даже не придется использовать внеэкранный буфер или писать код комбинирования пикселей. Обратной стороной является то, что вам нужно немного освоить 3D, если вы еще этого не знаете. :-)

Изменить для ответа на комментарий:

Комбинированная часть будет работать примерно так (в C ++ Java будет очень похожа - обратите внимание, что я не запускал приведенный ниже код через компилятор):

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

Чтобы ответить на ваш вопрос о трехмерном решении, вы просто нарисовали бы прямоугольники с соответствующими им текстурами (которые имели бы альфа-канал) друг над другом. Вы должны настроить систему на отображение в ортогональном режиме (для OpenGL: gluOrtho2D()).

0 голосов
/ 02 мая 2009

В старых 2d играх, таких как Diablo и Ultima Online, для этого используется метод составления спрайтов. Вы можете искать искусство в тех старых двухмерных изометрических играх, чтобы увидеть, как они это делали.

0 голосов
/ 31 декабря 2008

Так как меня попросили в комментариях также предоставить 3D-способ, вот некоторые из них, которые являются фрагментом кода, который я написал довольно давно. Это OpenGL и C ++.

Каждый спрайт будет предложено нарисовать сам. Используя шаблон адаптера, я бы объединил спрайты, то есть были бы спрайты, которые содержали бы два или более спрайтов с относительным положением (0,0) и один спрайт с реальным положением, имеющим все эти «подспрайты». *

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

tex_id_ является интегральным значением, которое определяет, какая текстура используется для OpenGL. Соответствующие части менеджера текстур таковы. Диспетчер текстур фактически эмулирует альфа-канал, проверяя, является ли считываемый цвет чисто белым (RGB of (ff, ff, ff)) - код loadFile работает с файлами BMP 24 бит на пиксель:

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

На самом деле это был маленький Jump'nRun, который я иногда разрабатывал в свободное время. Он также использовал режим gluOrtho2D (), кстати. Если вы оставите средства для связи с вами, я вышлю вам источник, если хотите.

...