スプライトアニメーションはゲーム開発の超基本“BREW”アプリケーション開発入門(5)(1/3 ページ)

2Dラスタデータを使った処理について解説。今回は、ゲームなどで多用される「スプライト」処理を利用した“アニメーション”に挑戦する!!

» 2009年11月27日 00時00分 公開
[末永貴一(エイチアイ),@IT MONOist]

 前回「ラスタ/ベクタ、2Dグラフィックスの基礎のキソ」は、2Dグラフィックスの基礎として「2Dベクタデータ」「2Dラスタデータ」の描画を行いました。今回はその続きとして、2Dラスタデータの利用をもう少し詳しく見ていきたいと思います。

 2Dラスタデータを使った処理は、ゲームなどでよく利用されており、単にデータをロードして描画する以外にも便利な機能が提供されています。

 今回はその利用を含め、さらに“アニメーションさせる”という部分にも触れたいと思います。


スプライトを利用する

 ゲームなどで多用される画像処理の1つに「スプライト」処理があります。ゲームでは画像をロードして、表示させるだけではなく、キャラクターがユーザーの操作によって動いたり、敵や背景がアニメーションするなど、描画フレームごとにグラフィックスが異なります。

 さらに、ユーザーがコントロールするキャラクターや敵キャラクターの動作は、状況によって変化するため、決まった画像を単に表示するのでなく、効率よくインタラクティブに描画する必要があります。

単純なアニメーションとゲームとの違い(a) 図1(a) 単純なアニメーションとゲームとの違い。アニメーションの場合、次の場面が決まっている
単純なアニメーションとゲームとの違い(b) 図1(b) 単純なアニメーションとゲームとの違い。ゲームの場合、次の場面がどうなるか分からない

 このように描画内容の一部を変更したり、画像を部品化したりして、データを効率よく用いる際に使用される技術がスプライトです。

 スプライトは、1画面の絵を構成する要素を複数の細かい部品(画像)にし、それらを画面上の任意の位置で合成して、表示する技術です。1枚の画像で1画面を作らないので、ゲームのようなインタラクティブな描画をするのに適した画像処理方法といえます。

1枚の絵とスプライトとの違い(a) 図2(a) 1枚の絵とスプライトとの違い。全体が1枚の絵
1枚の絵とスプライトとの違い(b) 図2(b) 1枚の絵とスプライトとの違い。全体が複数の絵で構成

ISpriteの利用

 BREWでは、専用のスプライト機能(スプライトエンジン)が提供されており、それを利用することで、スプライトを簡単に実現できます。

 スプライト機能を利用するためには「ISprite」を使います。「ISprite」には、大きく以下の機能が提供されています。

  1. オブジェクト表示機能……画面上をアニメーションするキャラクターやオブジェクトを扱う機能
  2. タイルマップ機能……背景を動的に作り出す機能

 この2つの機能を利用することによって、インタラクティブな2Dグラフィックス描画が可能になります。

コンパチブルビットマップの利用

 前回、ビットマップデータを描画したときには、単純に画像データをロードして、そのデータを画面転送しただけでした。ディスプレイは、「ピクセル」という単位で構成されたVRAMなどのメモリ上に「ピクセルバッファ」を持っており、これを「デバイスビットマップ」と呼びます。通常、プログラムから描画処理を行う場合、このデバイスビットマップへの処理が必要となります。

 一方、スプライトの場合は、ロードした画像を合成する必要があるため、合成用の領域を確保する必要があります。この領域は、デバイスビットマップとの互換性を有し、「コンパチブルビットマップ」と呼ばれます。

 コンパチブルビットマップを作成するには、「IBITMAP_CreateCompatibleBitmap()」を使います。

int IBITMAP_CreateCompatibleBitmap(IBITMAP *po, IBitmap **ppIBitmap, uint16 w, uint16 h); 

 第1引数に、元のデバイスビットマップを指定して、第3、4引数で指定した大きさで作成したコンパチブルビットマップを第2引数に格納します。ここで作成したコンパチブルビットマップをスプライト用の画像合成領域として利用します。

 以下は、スプライト用のビットマップデータをロードして、コンパチブルビットマップを作成するサンプルです(ソース1)。

void spriteInit(anime2d* pMe){
        IBitmap* bmp;           // BMP格納
        IBitmap* bmpScreen;     // デバイスビットマップ格納
        IBitmap* bmpScreenBuff; // コンパチブルビットマップ格納
        AEEBitmapInfo bmpInfo;  // BMP情報格納
        NativeColor    color;   // BMP色情報格納
        // ISpriteのインスタンス生成
        ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_SPRITE, (void**)&pMe->pISprite);
        // デバイスビットマップ取得
        bmpScreen = IDISPLAY_GetDestination(pMe->a.m_pIDisplay);
        // ビットマップデータのロード
        bmp = ISHELL_LoadBitmap(pMe->a.m_pIShell, "plane64.bmp");
        // ビットマップデータの情報取得
        IBITMAP_GetInfo(bmp, &bmpInfo, sizeof(bmpInfo));
        // コンパチブルビットマップ作成
        IBITMAP_CreateCompatibleBitmap(bmpScreen, &bmpScreenBuff, (uint16)bmpInfo.cx, n
(uint16)bmpInfo.cy);
        // コンパチブルビットマップにビットマップデータを転送
        IBITMAP_BltIn(bmpScreenBuff, 0, 0, (uint16)bmpInfo.cx, (uint16)bmpInfo.cy,
 bmp, 0, 0, AEE_RO_COPY);
        IBITMAP_Release(bmp);   // ビットマップデータ開放
        bmp = NULL;
 
 
        // 透過色設定
        IBITMAP_GetPixel(bmpScreenBuff, 0, 0, &color);       // 座標0,0の色情報取得
        IBITMAP_SetTransparencyColor(bmpScreenBuff, color);  // 取得した色を透過色に設定
        // スプライトバッファをエンジンに通知
        ISPRITE_SetSpriteBuffer(pMe->pISprite, SPRITE_SIZE_64X64, bmpScreenBuff);
        IBITMAP_Release(bmpScreenBuff);         // コンパチブルビットマップ開放
        bmpScreenBuff = NULL;
 
        // 描画ターゲットをスプライトエンジンに通知
        ISPRITE_SetDestination(pMe->pISprite, bmpScreen);
        IBITMAP_Release(bmpScreen);     // デバイスビットマップ開放
        bmpScreen = NULL;
} 
ソース1 スプライト用のビットマップデータをロードして、コンパチブルビットマップを作成

 「ISprite」を利用する場合には、以下のヘッダをインクルードする必要があります。

#include "AEESprite.h" 

 「ISprite」は別の関数でも利用するので、「Applet」構造体の下位にメンバを追加しておきます。

ISprite *pISprite;                      // ISprite格納 

 多少分かりにくい部分もあるかと思いますので、以下に処理の流れをまとめます(図3)。

スプライト用のビットマップデータをロードして、コンパチブルビットマップを作成する流れ 図3 スプライト用のビットマップデータをロードして、コンパチブルビットマップを作成する流れ

 ここでのポイントは、読み込んだスプライト用BMPデータを、「IBITMAP_BltIn()」で作成したコンパチブルビットマップに転送し、それを「ISPRITE_SetSpriteBuffer()」で「ISprite」に設定している部分です。

 単に、BMPデータを描画するだけであれば前回紹介した「IDISPLAY_BitBlt()」を利用すればよいのですが、ここでは任意のコンパチブルビットマップに対しての転送になるので、「IBITMAP_BltIn()」を利用しています。そして、スプライト処理はBREWのスプライトエンジン側のバッファで行われるため、スプライトエンジンのバッファに処理対象を設定する必要があります。

 最後に、「ISPRITE_SetDestination()」で最終的な描画ターゲットを指定しています。これにより、コンパチブルビットマップで行われた合成結果を、デバイスビットマップに対して転送することになります。実際の転送は、スプライト処理後に行います(詳しくは後述)。

 また、コンパチブルビットマップなどの変数を「IBITMAP_Release()」ですぐに開放していますが、特に問題はありません。BREWのAPIリファレンスを見てみると以下のように解説があります。

スプライトエンジンはビットマップに対して、「AddRef」と「Release」を適宜コールします。これにより、コール側ではビットマップに対して、自由に「Release」関数をコールでき、その後の管理を行う必要はなくなります。スプライトエンジンが、ビットマップ操作を完了すると、ビットマップは開放されます。

 まず、前提としてBREWでは、あるインスタンスが内部的に異なる複数のインスタンスから参照されている可能性があるため、参照カウントという形で参照インスタンスの数を管理しています。そのため、開放処理には参照カウントのデクリメントが必要で、参照カウントが0になると、インスタンスが実際に開放されます。つまり、「Release()」は参照カウントをデクリメントする関数で、ここではプログラム側からの参照とスプライトエンジン側からの参照があるため、プログラム側から「Release()」を呼んでも、スプライトエンジン側の参照が有効なため、ビットマップの開放は実際には行われていないことになります。

 スプライト処理は、読み込んだ画像をスプライトエンジン側でいろいろと操作するため、画像の読み込みから処理、開放に至るプロセスが長くなりがちです。このため、プログラム側のインスタンス参照を任意のタイミングで開放できれば、面倒な開放のタイミングを気にすることなく、スプライトエンジンに任せることができるという配慮がなされていると考えることができます。

※注1:サンプルプログラムでは、エラー処理を省略していますが、実際には適宜エラー処理を実装する必要があります。


       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.