第15回:背景描画のテクニック

 敵の作成が一通り終わったので、背景をゲーム画面に表示させることにしましょう。

 基準となるスプライトの縦幅を1ラインとし、上からライン単位でスプライトを配置していきます。1フレームが経過してExec()が呼び出されるごとに、描画開始位置を数ピクセル(スクロールスピード次第で調整)ずらした状態で再び配列していき、描画領域の外に配置していたラインが完全にウィンドウに表示された段階で、さらに1ラインを描画領域の外に追加します。あとはこの処理を繰り返していけば自然なスクロール処理が行われます。横スクロールや全方位スクロールもこのテクニックの応用に過ぎません。

 このプログラム例では、数ラインに一回、特定のグラフィックを配置するというおおざっぱなものとなっていますが、配列にデータを格納し、ライン番号に対応するスプライトグラフィックを描画すれば、一般的なゲームと遜色のない背景処理が行えるでしょう。

#pragma once

#include "GameDef.h"

#define BACKGROUND_PRIORITY 5000

class CBackGround : public CGameObject
{
private:
    CTexture tex;

    int linepos;    // 最上部のライン管理番号
    float pos_y;
    CSprite base_left, base_right;
    CSprite pipe_left, pipe_right;
public:
    CBackGround(void);
protected:
    virtual void Init();
    virtual void Exec();
};
#include "BackGround.h"

// 1ライン当たりの縦幅
#define LINEHEIGHT 150.0f

CBackGround::CBackGround(void)
{
    // 正気状態で、最上部のライン管理番号が何番になるかを計算
    linepos = (int)((480.0f + LINEHEIGHT) / LINEHEIGHT) + 1;

    // 一番上のラインを描画する位置
    pos_y = -LINEHEIGHT;
}

void CBackGround::Init()
{
    // テクスチャの読み込み・関連づけ
    tex.Load(_T("data\\bg.png"));

    base_left.SetTexture(&tex);
    base_left.SetSpriteRect(0, 0, 320, 150);
    base_left.SetCenterPosition(CP_TOP | CP_LEFT);

    base_right.SetTexture(&tex);
    base_right.SetSpriteRect(0, 150, 320, 300);
    base_right.SetCenterPosition(CP_TOP | CP_RIGHT);

    pipe_left.SetTexture(&tex);
    pipe_left.SetSpriteRect(320, 0, 448, 30);
    pipe_left.SetCenterPosition(CP_TOP | CP_LEFT);

    pipe_right.SetTexture(&tex);
    pipe_right.SetSpriteRect(320, 30, 448, 60);
    pipe_right.SetCenterPosition(CP_TOP | CP_RIGHT);
}

void CBackGround::Exec()
{
    // ラインごとのスプライト配置
    int bc = linepos;
    for(float y = pos_y; y < 480.0f; y += LINEHEIGHT, bc--){
        if(bc % 4 == 0){
            // ライン管理番号が4で割り切れる(つまり4回に1回)
            // のであれば、特別なスプライトを配置する
            base_left.Draw(0.0f, y);
            base_right.Draw(640.0f, y);
        }else{
            for(float yy = 0; yy < 150.0f; yy += 30.0f){
                pipe_left.Draw(0.0f, y + yy);
                pipe_right.Draw(640.0f, y + yy);
            }
        }
    }

    // スクロール処理
    pos_y += 2.0f;
    if(pos_y >= 0.0f){
        // 一番上のラインが描画領域に入ったのであれば
        // 新しいラインを追加するため、ライン管理番号を一増やす
        linepos++;
        pos_y = -LINEHEIGHT;
    }
}