第5章 キャラクタ構造体を使う

5-4.キャラクタ構造体を分割してメモリ使用量を減らす方法

シューティングゲームのように、様々な動きをする敵キャラを制御するプログラムを考える。例えば5種類の動きをする敵キャラを一定時間ごとにランダムに出現させ、最大50匹まで同時に出現するというプログラムを作る場合、敵キャラの情報を格納するキャラクタ構造体と、構造体配列は次のようになる。

今までのプログラム

#define ENEMY_MAX 50

/* ステータス情報構造体 */
typedef struct _STATUS {
    int type;           // 敵の種類(0〜4の5種類)
    int x, y;           // 表示座標
    int mx, my;         // 移動量
    int life;           // ライフ(0なら死亡)
    RECT rect;          // 画像の矩形
    RECT hitrect;       // 当たり判定用の矩形
    int width, height;  // キャラクタの幅と高さ
} STATUS;

/* 敵キャラ制御用変数(配列) */
STATUS ENEMY[ENEMY_MAX];

このように作成した敵キャラ用構造体配列が、メモリ上でどれだけの容量を使うかを考える。
Windows環境では、各データ型で必要なサイズは次のとおりである。

#include <stdio.h>
#include <windows.h>

void main(void)
{
    printf("char = %d byte\n", sizeof(char));
    printf("int = %d byte\n", sizeof(int));
    printf("long = %d byte\n", sizeof(long));
    printf("float = %d byte\n", sizeof(float));
    printf("double = %d byte\n", sizeof(double));
    printf("RECT = %d byte\n", sizeof(RECT));

}

char = 1 byte
int = 4 byte
long = 4 byte
float = 4 byte
double = 8 byte
RECT = 16 byte

※Windowsは32ビットOSなので、int型は2バイトではなく4バイトで扱われる。

ENEMY構造体配列1つの大きさは、int型7つとRECT型2つで、計60バイトであり、50個の配列なので、合計3000バイトのメモリ容量を必要とすることが分かる。

メモリ容量を節約するには?

ところがこの配列、よく見ると「無駄」があることに気がつく。それは、敵の種類が同じなら、画像の矩形情報は同じになるため、キャラクタごとに矩形情報を持つ必要はないということである。
よって、この構造体は次のように分割すると、無駄が省ける。

#define ENEMY_MAX 50
#define ENEMY_TYPE_MAX 5

/* ステータス情報構造体 */
typedef struct _STATUS {
    int type;           // 敵の種類(0〜4の5種類)
    int x, y;           // 表示座標
    int mx, my;         // 移動量
    int life;           // ライフ(0なら死亡)
} STATUS;
/* 矩形情報構造体 */
typedef struct _BLTSTATUS {
    RECT rect;          // 画像の矩形
    RECT hitrect;       // 当たり判定用の矩形
    int width, height;  // キャラクタの幅と高さ
} BLTSTATUS;


/* 敵キャラ制御用変数(配列) */
STATUS ENEMY[ENEMY_MAX];
/* 敵キャラ描画用変数(配列) */
BLTSTATUS ENEMY_BLT[ENEMY_BLT_MAX];

このように作成すると、矩形情報が敵キャラの種類の数(5つ)ですむため、メモリ使用量が減ることが分かる。

int型6つ = 24バイト
RECT型2つ + int型2つ = 32 + 8 = 40バイト
24×50 + 40×5 = 1400バイト

※構造体1つの場合は3000バイト必要だったので、1600バイト節約できた
※配列の個数が多くなっても、敵の種類が増えなければ24バイトずつしか増えない

プログラムでの使用方法

敵キャラ情報を2つに分けたため、使い方が若干変わる。どのように使えばいいかを簡単に紹介する。

《初期化》

// 敵キャラ情報初期化
for (i=0 ; i<ENEMY_MAX ; i++)
{
    敵キャラの数分(50個)、ライフを0クリアする
}

// 敵キャラ画像情報初期化
for (i=0 ; i<ENEMY_BLT_MAX ; i++)
{
    タイプ別(5個)に矩形、あたり判定用矩形をセットする
}

《移動処理》

for (i=0 ; i<ENEMY_MAX; i++)
{
    ENEMY[i].x += ENEMY[i].mx;
    ENEMY[i].y += ENEMY[i].my;
}

《描画処理》

for (i=0 ; i<ENEMY_MAX ; i++)
{
    hRet = g_pDDSBack->BltFast(ENEMY[i].x, ENEMY[i].y, g_pDDSChara, &ENEMY_BLT[ENEMY[i].type], DDBLTFAST_SRCCOLORKEY);
    if (hRet != DD_OK)
        return;
}

第5章4練習問題1(必須)

5−3までに作成したプログラムを改造する。ゴキブリを3匹以上表示し、適当に動き続けるようプログラムを修正しなさい。ただし、ステータス構造体をステータス用と描画用の2つに分割し、ゴキブリステータス用構造体変数は3つ以上の配列、ゴキブリBLT用構造体変数は1つで作成しなさい。

第5章4練習問題2(自由)

敵キャラとして、ゴキブリ以外のキャラクタをもう1種類追加しなさい。敵キャラ描画用構造体変数は2つの配列となる。


[ TOP ]