ここまでの冒険を記録しますか?

長岡技大ソフトウェア開発サークルの開発ブログ

【ゲーム製作入門】C/C++で簡単なRPGを作る④【DXライブラリ】

書き手:肥田野

DXライブラリでRPGのベースになるマップ画面やデータ管理などのあれこれの制作に挑戦してみます。

RPGとして完成する保証はありませんが、途中経過だけでも参考になれば幸いです。

この連載は前回までの内容を理解していることを前提に進めていきます。

前回プレイヤーの歩行を実装できたので、今度はマップ画面を描画する準備をします。

新たにMap.hを作成し、以下のように編集していきましょう。

≪Map.h≫

#pragma once

#include<DxLib.h>

#define CELL_WIDTH 40
#define CELL_HEIGHT 40
#define CELL_NUM_X 16
#define CELL_NUM_Y 12
#define WINDOW_Y 480
#define WINDOW_X 640

struct Cell{
    int gh;
};

class Map{
public:
    Cell cell[CELL_NUM_X][CELL_NUM_Y];
    int chipgh[(128/16)*(1248/16)];

    Map(){
        LoadDivGraph("BaseChip.png",(128/16)*(1248/16),128/16,1248/16,16,16,chipgh);
        for(int i=0;i<CELL_NUM_X;i++){
            for(int j=0;j<CELL_NUM_Y;j++){
                cell[i][j].gh = chipgh[i*CELL_NUM_X+j];
            }
        }
    }

    void View(){
        for(int i=0;i<CELL_NUM_X;i++){
            for(int j=0;j<CELL_NUM_Y;j++){
                DrawExtendGraph(i*CELL_WIDTH,j*CELL_HEIGHT,(i+1)*CELL_WIDTH,(j+1)*CELL_HEIGHT,cell[i][j].gh,TRUE);
            }           
        }
    }

    void All(){
        View();
    }
};

上から順に解説していきます。

#pragma once

これはヘッダファイルを二重にインクルードしてしまうミスを防止するために書く1文です。

#define CELL_WIDTH 40

前回はPlayer.hで定義していましたが、こちらに移動しました。

その代わり、Player.hの冒頭に「#include "Map.h"」を書き足してください。こうすることでPlayer.h内でもCELL_WIDTHを使うことができます。

#define CELL_NUM_X 16

画面上に表示するマスの横の数です。同様に縦も定義しています。

struct Cell{

新しい要素が出てきましたね。

しかし恐れることはありません。これは言ってしまえば「クラスの劣化版」のようなものなんです。

これは構造体と呼ばれるもので、クラスとは「関数を扱えない(=コンストラクタもない)」という違いなどがあります。

ではクラスを使えば良いのではないかと言われるとそれでも良いのですが、今回のようなデータしか扱わない場合はstructを使ったほうが分かりやすいかなと考え、こういう形をとりました。

今回はint型の変数が一つ(グラフィックハンドル)入っているだけですが、今後いくつか情報を増やしていく予定です。

Cell cell[CELL_NUM_X][CELL_NUM_Y];

こういう書き方もまだ解説していなかった気がしますね。

構造体CellをMapクラスの中で使うので、メンバ変数として宣言します。

構造体はこうして宣言してやるだけですぐ使えます。クラスのようにnew演算子を使うことはありません。

そして、配列の[]が二つ並んでいますね。これは二次元配列と呼ばれるもので、簡単に言えば配列一つ一つがさらに配列を持っている状態です。

方眼紙をイメージしながらデータ管理ができるので、マップの編集にはうってつけでしょう。

LoadDivGraph("BaseChip.png",(128/16)*(1248/16),128/16,1248/16,16,16,chipgh);

第1回で解説した、画像を分割して読み込む関数です。

今回分割するのはこちらになります。f:id:NUT_SoftwareDevelopper:20150511202328p:plain

注意!(16.7.9追記)

記事の通りにコーディングしたところ、正しく画像が表示されなかったという報告がありました。

当方で原因を調査したところ、上の画像をアップロードした際に、画像サイズが大きすぎたため自動で圧縮がかけられ、ファイルサイズが変わってしまったことが判明しました。

対処法としては、WOLFRPGエディターをDLしてそこからオリジナルの画像を拝借するか、ウディタ用の素材としてグラフィッカーさんがアップロードしているものを使わせていただくことを推奨します。

この時、ファイルサイズによってLoadDivGraphの引数が変わることに注意してください。

WOLFRPGエディターに標準装備されているマップチップ画像です。これ以外にも「ウディタ マップチップ」などで検索すれば、オリジナルの素材を投稿されている方が大勢いらっしゃるので、好きな画像を探すのも楽しいですよ。

分割した画像はメンバ変数の配列chipghに流し込みます。ちなみに引数の計算式は、画像サイズが128×1248で、チップ1つのサイズが16×16なのでこの式になります。

もし違うサイズの画像を使う場合は、適宜計算しなおしてください。

cell[i][j].gh = chipgh[i*CELL_NUM_X+j];

今回は画像を表示するだけで、まだマップの編集は行いません。

そのため、各マスのチップ画像のハンドルは、for文で適当に選択しています。全部同じでも、ランダムにしてもかまいません。どうせ次回には書き換えますからね(笑)

ただし、配列の範囲をはみ出さないように注意してくださいね。

Map.hが加わったことにより、Main.cppも少し変更しなければなりません。

≪Main.cpp≫

#include "DxLib.h"
#include "Map.h"
#include "Player.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPSTR lpCmdLine, int nCmdShow ){
    ChangeWindowMode(TRUE);
    if( DxLib_Init() == -1 )return -1 ;
    SetDrawScreen(DX_SCREEN_BACK);
    
    Player* pl = new Player();
    Map* map = new Map();
    
    while(ProcessMessage() != -1 && !ScreenFlip() && !ClearDrawScreen()){
        int startTime = GetNowCount();

        map->All();
        pl->All();
        
        if(CheckHitKey(KEY_INPUT_ESCAPE) == 1)break;
        int endTime = GetNowCount();
        WaitTimer((1000/60)-(endTime-startTime));
    }

    DxLib_End() ;
    return 0 ;
}

Map.hをインクルードして、new演算子で実体化しています。

while文内でも処理を追加しています。これを忘れると、せっかく編集しても結果に反映されませんからね。

さて実行すると、混沌としていますがマップが表示されました。

次回はこのマップを具体的に編集していきます。

でも、どうやって編集すればよいのでしょう。ソースコード上で長々と番号をふっていくのでしょうか?

もちろんそんな面倒なことはしません。csvファイルと呼ばれる、表計算ソフトで扱える形式のファイルで編集し、それを取り込む方法をご紹介します。