Вопрос

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

Нужно ли мне:

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

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

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

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

  • Какой-нибудь другой метод?

Пожалуйста, порекомендуйте.

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

Поскольку в комментариях меня попросили также предоставить 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 (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().Если вы оставите средства связи с вами, я пришлю вам исходник, если хотите.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top