الصورة الرمزية للاعب قابلة للتخصيص في لعبة ثنائية الأبعاد

StackOverflow https://stackoverflow.com/questions/402541

  •  03-07-2019
  •  | 
  •  

سؤال

كيف يمكنني الحصول على هذه الوظيفة في لعبتي والتي من خلالها يمكن للاعبين تغيير تصفيفة شعرهم، ومظهرهم، ونمط ملابسهم، وما إلى ذلك، وبالتالي كلما ارتدوا قطعة ملابس مختلفة، يتم تحديث الصورة الرمزية الخاصة بهم بها.

هل علي أن:

  • اطلب من المصمم الخاص بي إنشاء جميع المجموعات الممكنة من الدروع وتسريحات الشعر والوجوه ككائنات متحركة (قد يتطلب هذا الكثير من العمل).

  • عندما يختار اللاعب الشكل الذي يجب أن يبدو عليه أثناء تقديمه للعبة، سيقوم الكود الخاص بي تلقائيًا بإنشاء هذا الكائن، وجميع المجموعات الممكنة من أغطية الرأس/الدروع مع ذلك الكائن.ثم في كل مرة يختارون فيها درعًا مختلفًا، يتم تحميل الكائن الخاص بمجموعة الدرع/المظهر تلك.

  • هل من الممكن تقسيم كائن الشخصية إلى مكونات، مثل الوجه والقميص والجينز والأحذية، ولها أبعاد البكسل لكل منها.ثم عندما يقوم اللاعب بتغيير خوذته، على سبيل المثال، نستخدم أبعاد البكسل لوضع صورة الخوذة في المكان الذي تكون فيه صورة الوجه عادةً.(أنا أستخدم Java لبناء هذه اللعبة)

  • هل هذا غير ممكن في الوضع ثنائي الأبعاد ويجب أن أستخدم ثلاثي الأبعاد لهذا؟

  • هل هناك طريقة أخرى؟

يرجى تقديم النصيحة.

هل كانت مفيدة؟

المحلول

لن تكون الأبعاد الثلاثية ضرورية لهذا الغرض، لكن خوارزمية الرسام الشائعة في العالم ثلاثي الأبعاد قد توفر لك بعض العمل من خلال IMHO:

تعمل خوارزمية الرسام عن طريق رسم الكائنات البعيدة أولاً، ثم الرسم الزائد باستخدام الكائنات الأقرب إلى الكاميرا.في حالتك، قد يتلخص الأمر في إنشاء مخزن مؤقت للكائن الخاص بك، وسحبه إلى المخزن المؤقت، والعثور على جزء الكائن التابع التالي (على سبيل المثال:درع أو ما إلى ذلك)، ورسم ذلك، والعثور على جزء الكائن التابع التالي (أي.علامة خاصة موجودة على الدرع)، وهكذا.عندما لا يكون هناك المزيد من الأجزاء التابعة، يمكنك طلاء الكائن الذي تم إنشاؤه بالكامل على الشاشة التي يراها المستخدم.

يجب أن تحتوي الأجزاء المجمعة على قناة ألفا (RGBA بدلاً من RGB) بحيث يمكنك فقط دمج الأجزاء التي لها قيمة ألفا محددة بقيمة من اختيارك.إذا لم تتمكن من القيام بذلك لأي سبب من الأسباب، فما عليك سوى التمسك بمجموعة RGB واحدة ستعاملها على أنها شفافة.

قد يؤدي استخدام الأبعاد الثلاثية إلى تسهيل عملية دمج الأجزاء بالنسبة لك، ولن تضطر حتى إلى استخدام مخزن مؤقت خارج الشاشة أو كتابة كود دمج وحدات البكسل.الجانب الآخر هو أنك تحتاج إلى تعلم القليل من الأبعاد الثلاثية إذا كنت لا تعرفها بالفعل.:-)

تحرير للرد على التعليق:

سيعمل الجزء المدمج إلى حد ما مثل هذا (في 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()).

نصائح أخرى

أحد العوامل الرئيسية التي يجب مراعاتها هو الرسوم المتحركة.إذا كان لدى الشخصية درع به وسادات كتف، فقد تحتاج وسادات الكتف هذه إلى التحرك مع جذعه.وبالمثل، إذا كان يرتدي أحذية طويلة، فيجب أن يتبعها نفس الدورات التي تتبعها الأقدام العارية.

ما تحتاجه بشكل أساسي لمصمميك هو أ ورقة العفريت يتيح لفنانيك رؤية جميع إطارات الرسوم المتحركة الممكنة لشخصيتك الأساسية.ثم تطلب منهم إنشاء تسريحات شعر مخصصة، وأحذية طويلة، ودروع، وما إلى ذلك.بناء على تلك الأوراق.نعم، يتطلب الأمر الكثير من العمل، ولكن في معظم الحالات، ستتطلب العناصر الحد الأدنى من إعادة الرسم؛الأحذية هي الشيء الوحيد الذي يمكنني رؤيته حقًا وهو يتطلب الكثير من العمل لإعادة إنشائه نظرًا لأنه يتغير عبر إطارات متعددة للرسوم المتحركة.كن قاسيًا مع العفاريت الخاصة بك، وحاول تقليل العدد المطلوب قدر الإمكان.

بعد أن تقوم بتجميع مكتبة من العناصر، يمكنك البدء في الغش.قم بإعادة تدوير نفس تسريحة الشعر واضبط لونها إما في Photoshop أو مباشرة في اللعبة باستخدام أشرطة التمرير في أداة إنشاء الشخصية الخاصة بك.

الخطوة الأخيرة، لضمان الأداء الجيد داخل اللعبة، هي تسوية جميع أوراق الرموز المتحركة للعناصر المختلفة في ورقة رموز متحركة واحدة يتم بعد ذلك تقسيمها وتخزينها في مخازن مؤقتة للصور المتحركة.

سأذهب مع الجيل الإجرائي الحل (#2).طالما أنه لا يوجد عدد محدود من الكائنات التي سيتم إنشاؤها، بحيث يستغرق الإنشاء وقتًا طويلاً.ربما تفعل التوليد عند الحصول على كل عنصر لتقليل الحمل.

نظرًا لأنه طُلب مني في التعليقات تقديم طريقة ثلاثية الأبعاد أيضًا، فإليك بعضها، وهو مقتطف من بعض التعليمات البرمجية التي كتبتها منذ بعض الوقت.إنه برنامج 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 على 24 بت لكل ملفات BMP بكسل:

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 () أيضًا، راجع للشغل.إذا تركت وسيلة للتواصل معك سأرسل لك المصدر إذا أردت.

تستخدم الألعاب ثنائية الأبعاد الأقدم مثل Diablo وUltima Online تقنية تركيب الصور المتحركة للقيام بذلك.يمكنك البحث عن أعمال فنية من هذا النوع من الألعاب متساوية القياس القديمة ثنائية الأبعاد لترى كيف قاموا بذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top