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

六花です。
https://komochiduki.net/ikupro/2020/03/09/259/の続き、7、8、9日目です。

シリーズリスト :
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/


この三日は諸事情により書くことが少ないのでまとめようと思います。

■7日目
判定処理(主に地形との接触判定)を娘にちゃんと説明できるだけの準備を私ができなかったので、AABB判定の説明だけして残りはタイピング練習ということになりました。
AABB判定で扱うのはx軸y軸に平行な辺しか持たない四角形同士のため、判定は軸に写像して行うことができます。
数直線上で比較することで視覚的に理解できるからか、深く悩むことなく(表面上は?)理解したようです。
いくつか例題を出してみましたが、悩みながらも正答したので後日もう一度訊ねて定着を図ろうと思います。

■8日目
この日の朝、起きる時間になっても起きられそうな様子ではなく寝入っていたため、疲労が溜まっていると判断。
本人は「難しいけど楽しい、もっとやりたい」と言うのですが、娘の年齢に対して極端に高度なことを教えている自覚はあるので、この日は普段の日課も免除の上で、自由なことをする日にしました。

■9日目
突貫工事の接触判定を作ったので私が説明しながら実装。
書きながらバグ仕込んでるなぁと思いつつも、私の頭もあまり回っていないのでそのまま実装することにしました。
修正は娘と一緒にやる予定です。

娘はタイピング練習で、大文字のアルファベットの他にローマ字入力を練習中です。
ひらがなで打てるようになったことで、国語で習った詩などがパソコンでも書けるようになって嬉しいらしく、躍起になってキーボードを叩いていました。

いわゆるHelloWorldには全く手を付けていない、教本通りでないやり方でここまで来ましたが、どこまで理解してくれているか、不安と期待でいっぱいです。


#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_ブロック
{
    e_ブロック ブロックID;

    // 位置(いち)
    double x;
    double y;

    // 判定の位置
    double 判定_x;
    double 判定_y;

    // 判定の大きさ(はば、たかさ)
    double 判定_w;
    double 判定_h;
};

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

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

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

    // 判定の位置
    double 判定_x;
    double 判定_y;

    // 判定の大きさ(はば、たかさ)
    double 判定_w;
    double 判定_h;

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

struct s_敵
{

};

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

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

int AABB(double A_x, double A_y, double A_w, double A_h, double B_x, double B_y, double B_w, double B_h)
{
    if (((A_x + A_w) < B_x) || (A_x > (B_x + B_w))) { return 0; }
    if (((A_y + A_h) < B_y) || (A_y > (B_y + B_h))) { return 0; }
    return 1;
}

bool マスくんをブロックが押しのける(s_ブロック& ブロック)
{
    double マスくん判定_x_world = マスくん.x + マスくん.判定_x;
    double マスくん判定_y_world = マスくん.y + マスくん.判定_y;
    double ブロック判定_x_world = ブロック.x + ブロック.判定_x;
    double ブロック判定_y_world = ブロック.y + ブロック.判定_y;

    // まずブロックとくっついているか調べて、くっついていなければ特に何もしない
    if (AABB(マスくん判定_x_world, マスくん判定_y_world, マスくん.判定_w, マスくん.判定_h, ブロック判定_x_world, ブロック判定_y_world, ブロック.判定_w, ブロック.判定_h) == 0) { return false; }

    // マスくんの1フレーム前の位置を、現在の速度を使って逆算する
    double マスくん_x_org = マスくん判定_x_world - マスくん.vx;
    double マスくん_y_org = マスくん判定_y_world - マスくん.vy;

    // 動く前の位置から、速度の分移動した位置の間で、まだブロックとくっついていないときと、もうブロックとくっついてしまったときの境目が存在する
    // その境目を探したい
    // vxあるいはvyが100%(1.0)のときはブロックとくっついているはず、また逆に0%(0.0)のときはブロックとくっついていないはず
    // そのため、90%から10%ずつ減らしてブロックから少しずつ遠ざけることによって、ブロックにくっつかないが一番ブロックに近い座標を探す
    for (double i_rate = 0.9; i_rate >= 0.0; i_rate -= 0.1)
    {
        double A_x_new = マスくん_x_org + マスくん.vx * i_rate;
        double A_y_new = マスくん_y_org + マスくん.vy * i_rate;

        // ブロックにくっつかない場合、その座標が探したいものなので、A_xとA_yに手に入れた答えを入れて、関数を終了する。
        if (AABB(A_x_new, A_y_new, マスくん.判定_w, マスくん.判定_h, ブロック判定_x_world, ブロック判定_y_world, ブロック.判定_w, ブロック.判定_h) == 0)
        {
            マスくん.x = A_x_new - マスくん.判定_x;
            マスくん.y = A_y_new - マスくん.判定_y;
            return true;
        };
    }

    return false;
}

int マスくんとブロックの存在判定(s_ブロック& ブロック)
{
    // ブロックとくっついているか
    if (マスくんをブロックが押しのける(ブロック) == true)
    {
        // マスくんの下側がブロックの上側より上にあるとき、マスくんはブロックの上に乗っている
        // 上に乗っている方が優先される
        if (マスくん.y + マスくん.判定_y + マスくん.判定_h < ブロック.y)
        {
            マスくん.vy *= double(0);
            マスくん.状態 = s_マスくん::e_状態::接地;
        }
        // マスくんの右側がブロックの左側より左にあったり、マスくんの左側がブロックの右側より右にあるとき、マスくんは壁側にくっついているといえる
        else if ((マスくん.x + マスくん.判定_x + マスくん.判定_w < ブロック.x) || (ブロック.x + ブロック.判定_x + ブロック.判定_w < マスくん.x))
        {
            マスくん.vx *= double(0);
        }

        return 1;
    }
    return 0;
}



// プログラムは 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)
        {
            ステージ_1.ブロック.at(i_x).at(i_y).x = i_x * ブロックの大きさ;
            ステージ_1.ブロック.at(i_x).at(i_y).y = i_y * ブロックの大きさ;
            ステージ_1.ブロック.at(i_x).at(i_y).判定_x = double(0);
            ステージ_1.ブロック.at(i_x).at(i_y).判定_y = double(0);
            ステージ_1.ブロック.at(i_x).at(i_y).判定_w = double(ブロックの大きさ);
            ステージ_1.ブロック.at(i_x).at(i_y).判定_h = double(ブロックの大きさ);

            int R, G, B, A;
            GetPixelSoftImage(マップ情報_1, i_x, i_y, &R, &G, &B, &A);

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

    マスくん.x = ステージ_1.スタート位置_x * ブロックの大きさ;
    マスくん.y = ステージ_1.スタート位置_y * ブロックの大きさ;
    マスくん.判定_x = 32;
    マスくん.判定_y = 26;
    マスくん.判定_w = 64;
    マスくん.判定_h = 81;

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

        // 地面の摩擦
        if (マスくん.状態 == s_マスくん::e_状態::接地)
        {
            マスくん.vx = double(0);
            マスくん.ay = double(0);
        }
        else
        {
            // 重力(じゅうりょく)
            マスくん.ay = +9.80665 / double(6);
        }

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

        if (keydata[KEY_INPUT_RIGHT] == 1)
        {
            マスくん.vx = double(+10);
        }
        if (keydata[KEY_INPUT_LEFT] == 1)
        {
            マスくん.vx = double(-10);
        }

        if (keydata[KEY_INPUT_UP] == 1 && マスくん.状態 != s_マスくん::e_状態::空中)
        {
            マスくん.vy -= double(25);
            マスくん.状態 = s_マスくん::e_状態::空中;
        }

        // 加速度を反映
        マスくん.vx += マスくん.ax;
        マスくん.vy += マスくん.ay;

        // 速度を反映
        マスくん.x += マスくん.vx;
        マスくん.y += マスくん.vy;


        カメラの左端 = マスくん.x - (ウインドウの大きさ_x / 2);
        if (カメラの左端 < 0) { カメラの左端 = 0; }
        if (カメラの左端 >= (ブロックの大きさ * 128 - ウインドウの大きさ_x)) { カメラの左端 = (ブロックの大きさ * 128 - ウインドウの大きさ_x); }

        // 判定処理
        int マスくんとブロックがくっついている数 = 0;
        for (int i_x = 0; i_x < 128; i_x += 1)
        {
            for (int i_y = 16 - 1; i_y >= 0; i_y -= 1)
            {
                if (ステージ_1.ブロック.at(i_x).at(i_y).ブロックID != e_ブロック::空気)
                {
                    マスくんとブロックがくっついている数 += マスくんとブロックの存在判定(ステージ_1.ブロック.at(i_x).at(i_y));
                }
            }
        }
        if (マスくんとブロックがくっついている数 == 0)
        {
            マスくん.状態 = s_マスくん::e_状態::空中;
        }

        // 背景の描画

        // ブロックの描画
        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.ブロック.at(i_x).at(i_y).ブロックID)
                {
                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;
                }
            }
        }

        // マスくんの描画
        DrawGraph(double(マスくん.x - カメラの左端), double(マスくん.y), 画像_マスくん_立ち, true);
        DrawGraph(double(マスくん.x - カメラの左端) + 25, double(マスくん.y) - 16, 画像_マスくんの手, true);

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

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

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

おすすめ

コメントを残す

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