プログラミングを春休みの自由研究にしてみる 6日目

六花です。
https://komochiduki.net/ikupro/2020/03/04/253/の続き、6日目です。

シリーズリスト :
1日目 : https://komochiduki.net/ikupro/2020/03/01/234/
2日目 : https://komochiduki.net/ikupro/2020/03/03/238/
3日目 : https://komochiduki.net/ikupro/2020/03/03/245/
4日目 : https://komochiduki.net/ikupro/2020/03/04/250/
5日目 : https://komochiduki.net/ikupro/2020/03/04/253/
6日目 : https://komochiduki.net/ikupro/2020/03/09/259/
7、8、9日目 : https://komochiduki.net/ikupro/2020/03/11/263/
10、11、12日目 : https://komochiduki.net/ikupro/2020/03/12/268/
13日目[本編1日目] : https://komochiduki.net/ikupro/2020/03/13/286/
14日目[本編2日目] : https://komochiduki.net/ikupro/2020/03/15/289/
15-21日目[本編3-9日目] : https://komochiduki.net/ikupro/2020/03/21/302/

投稿が遅れて申し訳ありません。
日常と娘への授業とその準備の両立がきつくなってきました。眠い。

今日は、昨日描いた娘のマップ情報をアプリケーション側で読み込んでブロックとして画面に表示する処理を作りました。

for文の説明と、二次元配列の復習をしながら私がコードを書いていきます。
途中、DXライブラリの関数を呼ぶ都合上、関数の値渡しと参照渡しの説明をしました。
ただし、アドレスやポインタなどの説明を省いています。
プログラミング言語はどのようなものであれ、端から覚えていくと大人だって辟易するくらい仕様が膨大です。
そのため、私は説明しなくていいことは後回しに後回しにしています。
(変数の型と関数だけはちゃんと理解しておかないといけないと思ったので初日に教えました。)

今回用意したマップチップはブロックの側面と天面が一体化した縦2横1の画像なので、下に位置するブロックから描画していかないと変な感じに表示されてしまいます。
そのため「上のブロックから描画したら良いか、下のブロックから描画したら良いか」を訊ねたところ「下から」と答えたので驚いたのですが、それは単に積み木を積む感覚で答えたのがたまたま正答したようです。
「なぜこのやり方がダメなのか」の例を出しやすいのがプログラミングの良いところです。
私は実際に上から描画するコードを書き、上のブロックを下のブロックが上書きしてぐちゃぐちゃになった画面を見せました。
最初は「何か違う」とだけ認識していた娘でしたが、何が違うのか、間をおいてちゃんと理解することができました。

今回のマップは初代マリオの1-1より小さいので、記述を簡単にするために全てのマップチップを素直に描画しています。(画面外のマップチップを描画しない、などの手段を取っていません。)
「カメラ(描画領域)の左端」という変数を設定し、ブロックに設定されたワールド座標をウインドウに表示するローカル座標に変換する処理を行うことでスクロールを実装しました。
……何度か読ませてわかったような素振りを見せていますが、実際に自分で書いてみたら多分「?」となると思います。

この日は接触判定の予告をして、終了となりました。


#include "DxLib.h"

#include <array>

constexpr int ウインドウの大きさ_x = 1920;
constexpr int ウインドウの大きさ_y = 1080;
constexpr int ブロックの大きさ = ウインドウの大きさ_y / 16;

int カメラの左上位置_x = 0;

enum class e_ブロック : int { 空気, 水, 草, 丸太, いちご };
enum class e_敵 : int { 未定義, こぐま, おやぐま, はりせんぼん, くじら };

struct s_画面
{
    enum class e_状態 : int { タイトル, ステージ選択, アクション, スタッフロール };
    e_状態 状態;
};

// どの敵がどこに出てくるか
struct s_敵_配置
{
    int x = -1;
    int y = -1;
    e_敵 敵ID = e_敵::未定義;
};

struct s_ブロック
{

};

struct s_マスくん
{
    // 位置(いち)
    double x;
    double y;

    // 速さ(はやさ)、速度(そくど)
    double vx;
    double vy;

    // 加速度(かそくど)
    double ax;
    double ay;

    // 状態(じょうたい)
    enum class e_状態 : int { 立ってる, 空中, 水中 };
    e_状態 状態;
};
s_マスくん マスくん;

struct s_敵
{

};

struct s_ステージ
{
    // 定義の部分
    std::array<std::array<e_ブロック, 16>, 128> ブロックID; // [128][16]の配列、拡張性はない
    int スタート位置_x;
    int スタート位置_y;
    std::array<s_敵_配置, 10> 敵_配置;

    // 今の敵の状態
    std::array<s_敵, 10> 敵;
};
s_ステージ ステージ_1;





// プログラムは WinMain から始まります
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    ChangeWindowMode(true); // ウインドウモードで起動
    SetWindowStyleMode(7); // 7:最大化ボタンが存在するウインドウモードに変更

    SetGraphMode(1920, 1080, 32);
    SetWindowSize(1920, 1080); // 初期ウインドウの大きさ

    SetWindowSizeChangeEnableFlag(true, true); // ウインドウサイズ変更を可能にする(縦横比変更不可)

    if (DxLib_Init() == -1)     // DXライブラリ初期化処理
    {
        return -1;          // エラーが起きたら直ちに終了
    }

    SetTransColor(255, 255, 255);
    SetUsePremulAlphaConvertLoad(true); // 画像を読み込んだ後、乗算済みアルファ画像に変換する設定を有効にする
    SetDrawScreen(DX_SCREEN_BACK);//裏画面

    // 画像読み込み
    int 画像_マスくん_立ち = LoadGraph("../../data/マスくん_立ち.png");
    int 画像_マスくんの手 = LoadGraph("../../data/マスくんの手.png");
    int マップ情報_1 = LoadSoftImage("../../data/マップ_1ステージ目.png");
    int 画像_ブロック_草 = LoadGraph("../../data/ブロック_草.png");
    int 画像_ブロック_丸太 = LoadGraph("../../data/ブロック_丸太.png");
    int 画像_ブロック_いちご = LoadGraph("../../data/ブロック_いちご.png");

    // マップ情報をブロックに変換
    for (int i_x = 0; i_x < 128; i_x += 1)
    {
        for (int i_y = 0; i_y < 16; i_y += 1)
        {
            int R, G, B, A;
            GetPixelSoftImage(マップ情報_1, i_x, i_y, &R, &G, &B, &A);

            /**/ if (R == 255 && G == 255 && B == 255) { ステージ_1.ブロックID.at(i_x).at(i_y) = e_ブロック::空気; }
            else if (R == 207 && G == 143 && B ==  79) { ステージ_1.ブロックID.at(i_x).at(i_y) = e_ブロック::草; }
            else if (R == 128 && G ==  64 && B ==   0) { ステージ_1.ブロックID.at(i_x).at(i_y) = e_ブロック::丸太; }
            else if (R ==   0 && G == 254 && B ==   0) { ステージ_1.ブロックID.at(i_x).at(i_y) = e_ブロック::いちご; }
            else if (R ==   0 && G == 128 && B == 255) { ステージ_1.ブロックID.at(i_x).at(i_y) = e_ブロック::水; }
        }
    }

    int カメラの左端 = 0;
    while (ProcessMessage() != -1)
    {
        ClearDrawScreen(); // 画面を掃除する
        DrawBox(0, 0, ウインドウの大きさ_x, ウインドウの大きさ_y, GetColor(255, 255, 255), true);

        // キーボードの入力を取得する
        char keydata[256];
        GetHitKeyStateAll(keydata);

        if (keydata[KEY_INPUT_RIGHT] == 1)
        {
            カメラの左端 += 30;
        }
        if (keydata[KEY_INPUT_LEFT] == 1)
        {
            カメラの左端 -= 30;
        }
        if (カメラの左端 < 0) { カメラの左端 = 0; }
        if (カメラの左端 >= (ブロックの大きさ * 128 - ウインドウの大きさ_x)) { カメラの左端 = (ブロックの大きさ * 128 - ウインドウの大きさ_x); }

        // 背景の描画

        // ブロックの描画
        for (int i_x = 0; i_x < 128; i_x += 1)
        {
            for (int i_y = 16 - 1; i_y >= 0; i_y -= 1)
            //for (int i_y = 0; i_y < 16; i_y += 1)                       
            {
                int 左上座標_x = ブロックの大きさ * i_x;
                int 左上座標_y = ブロックの大きさ * i_y;

                switch (ステージ_1.ブロックID.at(i_x).at(i_y))
                {
                case e_ブロック::空気: {  } break;
                case e_ブロック::草: { DrawExtendGraph(左上座標_x - カメラの左端, 左上座標_y - ブロックの大きさ, 左上座標_x + ブロックの大きさ - カメラの左端, 左上座標_y + ブロックの大きさ, 画像_ブロック_草, true); } break;
                case e_ブロック::丸太: { DrawExtendGraph(左上座標_x - カメラの左端, 左上座標_y - ブロックの大きさ, 左上座標_x + ブロックの大きさ - カメラの左端, 左上座標_y + ブロックの大きさ, 画像_ブロック_丸太, true); } break;
                case e_ブロック::いちご: { DrawExtendGraph(左上座標_x - カメラの左端, 左上座標_y - ブロックの大きさ, 左上座標_x + ブロックの大きさ - カメラの左端, 左上座標_y + ブロックの大きさ, 画像_ブロック_いちご, true); } break;
                case e_ブロック::水: {  } break;
                }
            }
        }


        ScreenCopy(); // 描画を反映する
    }

    DxLib_End();                // DXライブラリ使用の終了処理

    return 0;               // ソフトの終了 
}

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です