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

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

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

書き手:肥田野

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

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

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

前回はプレイヤーの歩行アニメーションを実装したので、今回は実際に座標を動かしてみます。

今回説明するコードです。今回もPlayer.hのみになります。

#include<DxLib.h>

#define WALKTIME 15
#define CELL_WIDTH 40
#define CELL_HEIGHT 40

class Player{
public:
    int gh[12];
    int width,height;
    int walkVec;
    int animCount;
    bool walkFlag;
    int x,y;//グラフィックを描画する座標
    int targetX,targetY;//プレイヤーが次に向かうべき座標。現在位置の隣のマスのどれか
    int speed;
    Player(){
        LoadDivGraph("Chara.png",12,3,4,20,28,gh);
        GetGraphSize(gh[0],&width,&height);
        walkVec = 2;
        walkFlag = false;
        animCount = 0;
        x = 0;
        y = 0;
        speed = 1;
        targetX = 0;
        targetY = 0;
    }

    void AnimationView(int animState,int firstNum){
        if(animState == 0)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[firstNum],TRUE);
        if(animState == 1)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[firstNum+1],TRUE);
        if(animState == 2)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[firstNum+2],TRUE);
        if(animState == 3)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[firstNum+1],TRUE);
    }

    void Move(){
        walkFlag = false;
        
        if(targetX == x && targetY == y){//移動中でなければ
            if(CheckHitKey(KEY_INPUT_DOWN)){
                walkFlag = true;
                walkVec = 2;
                targetY+=CELL_HEIGHT;//targetYを1マス分移動
            }else if(CheckHitKey(KEY_INPUT_LEFT)){
                walkFlag = true;
                walkVec = 4;
                targetX-=CELL_WIDTH;
            }else if(CheckHitKey(KEY_INPUT_RIGHT)){
                walkFlag = true;
                walkVec = 6;
                targetX+=CELL_WIDTH;
            }else if(CheckHitKey(KEY_INPUT_UP)){
                walkFlag = true;
                walkVec = 8;
                targetY-=CELL_HEIGHT;
            }else{
                animCount = 0;//キーが押されておらず、移動が完了していればanimCountをリセット
            }
        }else{
            walkFlag = true;
        }
        
        if(y < targetY)y+=speed;
        if(x > targetX)x-=speed;
        if(x < targetX)x+=speed;
        if(y > targetY)y-=speed;
    }

    void View(){

        int animState = animCount/WALKTIME;//animCountがWALKTIMEの公倍数になるたびにanimStateが1増える

        if(animState == 4){//animStateが4になったらリセット
            animCount = 0;
            animState = 0;
        }
        
        if(walkFlag){
            if(walkVec == 2)AnimationView(animState,0);
            if(walkVec == 4)AnimationView(animState,3);
            if(walkVec == 6)AnimationView(animState,6);
            if(walkVec == 8)AnimationView(animState,9);
            animCount++;
        }else{
            if(walkVec == 2)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[1],TRUE);
            if(walkVec == 4)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[4],TRUE);
            if(walkVec == 6)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[7],TRUE);
            if(walkVec == 8)DrawExtendGraph(x,y+height/2,x+width*2,y+height*2+height/2,gh[10],TRUE);
        }
    }

    void All(){
        Move();
        View();
    }
};
#define CELL_WIDTH 40 #define CELL_HEIGHT 40

まず前回解説したマクロANIMATION_VIEWですが、関数にまとめたほうが分かりやすいと感じたので変更しました。

代わりにCELL_WIDTHとCELL_HEIGHTを追加しています。

RPGでは一般に、マップが格子状のマス目から構成されており、どのマスにどの画像を表示するか、どんなイベントを配置するかなどをプログラムしていきます。

その基準となるマスのサイズを設定したのがこの二つのマクロです。

void AnimationView(int animState,int firstNum){

前回マクロを使っていた箇所ですが、今回からは関数を使うことにしました。

それに伴い、引数animStateを追加しています。

int targetX,targetY;

x,yの他にもう二つ、座標を入れる変数を宣言しました。

これはプレイヤーが存在する場所ではなく、次に移動する移動先の座標になります。原則、x,yの±CELL_WIDTH(またはCELL_HEIGHT)の値をとります。

if(targetX == x && targetY == y){

Move関数のif文です。

キー入力をするとtragetXまたはtargetYがマス目のサイズ分動くので、この条件式を満たさなくなります。

逆に、プレイヤーが移動していないときはこの条件を満たすので、キー入力を受け付けるようになります。

if(y < targetY)y+=speed;

プレイヤーのtarget座標が現在の座標より大きい時、現在の座標がtarget座標に一定の速度で近づきます。

4方向で処理がすべて違うので注意です。

if(walkVec == 2)AnimationView(animState,0);

新しく追加した引数には、View関数内で定義したanimStateをそのまま使います。

今回は短いですがここまでです。

無事にプレイヤーが動き出したでしょうか。次回はマップ画像を表示するための基本的な仕組みについて解説していきます。