質問
どうすればゲーム内でその機能を使用して、プレイヤーが髪型、外見、服のスタイルなどを変更できますか?したがって、服を着るたびにアバターが更新されます。
私がすべき:
-
デザイナーに鎧、髪型、顔のすべての可能な組み合わせをスプライトとして作成させます(これは大変な作業になる可能性があります)。
-
ゲームの紹介中にプレーヤーがどのように見えるかを選択すると、私のコードはこのスプライトと、ヘッドギア/アーマーとそのスプライトの可能なすべての組み合わせを自動的に作成します。その後、異なるアーマーを選択するたびに、そのアーマー/ルックの組み合わせのスプライトがロードされます。
-
キャラクターのスプライトを顔、シャツ、ジーンズ、靴などのコンポーネントに分割し、それぞれのピクセルサイズを設定することは可能ですか?次に、たとえば、プレーヤーがヘルメットを変更するたびに、ピクセルサイズを使用して、ヘルメットの画像を通常の顔画像の代わりに配置します。 (このゲームをビルドするためにJavaを使用しています)
-
これは2Dでは不可能であり、3Dを使用する必要がありますか?
- 他の方法?
アドバイスしてください。
解決
これには3Dは必要ありませんが、3Dの世界で一般的なペインタアルゴリズムを使用すると、作業を節約できます。
ペインターアルゴリズムは、最も遠いオブジェクトを最初に描画し、次にカメラに近いオブジェクトで上書きします。あなたの場合、それはあなたのスプライトのバッファを生成し、バッファに描画し、次の依存するスプライト部分(すなわち鎧またはその他)を見つけ、それを描画し、次の依存するスプライト部分(すなわち特別な鎧の上にあるサイン)など。依存する部分がなくなったら、生成された完全なスプライトをユーザーに表示されるディスプレイにペイントします。
組み合わせたパーツには、選択した値に設定されたアルファ値を持つパーツのみを結合できるように、アルファチャネル(RGBではなくRGBA)が必要です。何らかの理由でそれができない場合は、透明として扱うRGBの組み合わせを1つだけ使用してください。
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()
)。
他のヒント
考慮すべき主な要因の1つは、アニメーションです。キャラクターが肩パッド付きの鎧を持っている場合、それらの肩パッドは胴体とともに移動する必要があります。同様に、彼がブーツを履いている場合、それらは素足を隠すのと同じサイクルに従わなければなりません。
本質的にデザイナーに必要なのは、スプライトシートこれにより、アーティストはベースキャラクターのアニメーションのすべての可能なフレームを見ることができます。次に、それらのシートに基づいて、カスタムヘアスタイル、ブーツ、アーマーなどを作成します。はい、多くの作業がありますが、ほとんどの場合、要素は最小限の再描画を必要とします。ブーツは、アニメーションの複数のフレームにわたって変化するため、再作成するのに本当に多くの作業を要することがわかる唯一のものです。スプライトを無慈悲にして、必要な数をできるだけ減らすようにしてください。
要素のライブラリを蓄積したら、不正行為を開始できます。同じヘアスタイルをリサイクルし、Photoshopで、またはキャラクター作成者のスライダーを使用してゲーム内で直接その色を調整します。
ゲーム内で良好なパフォーマンスを確保するための最後のステップは、すべての異なる要素のスプライトシートを単一のスプライトシートにフラット化してから、分割してスプライトバッファーに保存することです。
プロシージャ生成ソリューション(#2)を使用します。生成するスプライトの量が制限されていない限り、生成に時間がかかりすぎます。たぶん、負荷を下げるために、各アイテムが取得されたときに生成を行います。
私も3Dの方法を提供するようにコメントで求められたので、ここにいくつかあります。それはかなり前に書いたコードの抜粋です。 OpenGLとC ++です。
各スプライトは自分自身を描くように求められます。 Adapterパターンを使用して、スプライトを結合します。つまり、(0,0)相対位置を持つ2つ以上のスプライトと、それらすべての「サブ」スプライトを持つ実際の位置を持つ1つのスプライトを保持するスプライトがあります。
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などの古い2Dゲームは、スプライト合成技術を使用してこれを行います。これらの古い2Dアイソメトリックゲームのアートを検索して、どのように実行されたかを確認できます。