ロックマンエグゼのような3*3マスを移動できるようにしたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Ouxiy
記事: 169
登録日時: 4ヶ月前

ロックマンエグゼのような3*3マスを移動できるようにしたい

#1

投稿記事 by Ouxiy » 4ヶ月前

\環境
visual studio 2019
DXライブラリ
C言語、C++

コード:

#include "DxLib.h"



int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey() {
    char tmpKey[256]; // 現在のキーの入力状態を格納する
    GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
    for (int i = 0; i < 256; i++) {
        if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら
            Key[i]++;     // 加算
        }
        else {              // 押されていなければ
            Key[i] = 0;   // 0にする
        }
    }
    return 0;
}

    // プログラムは WinMain から始まります
    int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        SetGraphMode(1300, 680, 32); // ウィンドウの大きさを指定
        ChangeWindowMode(TRUE);

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


        //キー取得用配列
        //char key[256];
        //1. 3x3マスの2次元配列
        int pos[][3] = {
                        {0, 0, 0 },
                        {0, 0, 0 },
                        {0, 0, 0 }
        };

        int playerX = 0; // キャラのX座標
        int playerY = 300; // キャラのY座表
        //double a[2][2];//移動制限のための配列の変数
        int x = 0;
        int y = 0;



        //グラフィックハンドル格納用配列
        int gh[12];
        LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh);
        DrawGraph(playerX, playerY, gh[8], FALSE);// プレイヤーの画像を描画



        while (ScreenFlip() == 0 && ProcessMessage() == 0&& gpUpdateKey() == 0) {

                int 加算=70;
                int b = 70;


                // カーソルキーの右が押されている
                if (Key[KEY_INPUT_RIGHT]==1) {



                            playerX = playerX + 加算; // プレイヤーのX座標を加算//左だけplayerX2とするとfor文ではないので、一回しか起きない、playerXだとplayerX = playerX + 加算より、加算されたあとのplayerXが再び右辺のplayerXに入り加算されるを繰り返すため∞に右に行けるのだ
                            // 画面に出力
                            ScreenFlip();
                            // 画面をクリア
                            ClearDrawScreen();
                            // プレイヤーの画像を描画

                            DrawGraph(playerX, playerY, gh[2], true);//DrawGraphはLoadDivGraphにより12分割された配列の一つである、gh[8]を描画できる関数である。
                        }

                else { while (DrawGraph(playerX, playerY, gh[8], true)); }//右を押されて加算されていく中で、もし加算されない間はキャラの描画はgh[8]にする。elseを付けることで条件を否定できる。

                if (Key[KEY_INPUT_UP] == 1) {
                    playerY = playerY - b; // プレイヤーのY座標を加算

                    // 画面に出力
                    ScreenFlip();
                    // 画面をクリア
                    ClearDrawScreen();
                    // プレイヤーの画像を描画
                    DrawGraph(playerX, playerY, gh[5], true);
                }


                    if (Key[KEY_INPUT_LEFT] == 1){
                        playerX = playerX - 加算; // プレイヤーのX座標を加算


                        // 画面をクリア
                        ClearDrawScreen();
                        // プレイヤーの画像を描画
                        DrawGraph(playerX, playerY, gh[1], true);

                    }


                    if (Key[KEY_INPUT_DOWN] == 1) {
                        playerY = playerY + b; // プレイヤーのY座標を加算

                        // 画面に出力
                        ScreenFlip();
                        // 画面をクリア
                        ClearDrawScreen();
                        // プレイヤーの画像を描画
                        DrawGraph(playerX, playerY, gh[11], true);
                    }

        }

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

        return 0;                // ソフトの終了 

    }
実行したいこと
ロックマンエグゼのようにキャラクターを3*3マスを自由に移動できるようなプログラムをfor (y = 0;y < 3; y++) {
for (x = 0; x < 3; x++) {を用いて可能にしようと思うのですが、どのようにして関数if (Key[KEY_INPUT_RIGHT]==1) {}に加えればよいか悩んでいます。


以下に間違ってはいますが、私なりに書いたプログラムを書かせて頂きます。

                 

コード:

// カーソルキーの右が押されている
if (Key[KEY_INPUT_RIGHT]==1) {
playerX = playerX + 加算; 
                               double[1][1]=playerX;
// 画面に出力
ScreenFlip();
// 画面をクリア
ClearDrawScreen();
// プレイヤーの画像を描画

                        DrawGraph(double[1][1], playerY, gh[2], true);         
}
右キーによりキャラが座標[1][1]に移動して、座標[1][1]での描画される画像は gh[2]となる。ことを表したプログラムです。これにfor (y = 0;y < 3; y++) {
for (x = 0; x < 3; x++) {を加えれば、解決するのではないかと思っていたりします。
以上のプログラムにおいて、なぜそのように置けないのか、もしプログラムのまま置くとしたら何が必要かを是非教えてほしいです。というのも今後の自己解決力を高めるためです。ご協力お願いいたします。

もしforの文と配列で今回行いたいことを全コードを書いていただけるならば、なぜそのようにおけるのか引数を使う場合は()、どんな処理をさせたいかは{}を使うなどのように細かく解説して頂けると今後の自己解決力に繋がるのでよろしくお願いいたします。

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#2

投稿記事 by みけCAT » 4ヶ月前

質問に答えるのは難しそうな気がしたので、
とりあえずプレイヤーが3*3マスを移動できるプログラムを適当に書いてみました。

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey() {
    char tmpKey[256]; // 現在のキーの入力状態を格納する
    GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
    for (int i = 0; i < 256; i++) {
        if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら
            Key[i]++;         // 加算
        }
        else {                // 押されていなければ
            Key[i] = 0;       // 0にする
        }
    }
    return 0;
}

// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    SetGraphMode(1300, 680, 32); // ウィンドウの大きさを指定
    ChangeWindowMode(TRUE);

    if (DxLib_Init() == -1) // DXライブラリ初期化処理
    {
        return -1; // エラーが起きたら直ちに終了
    }
    // 描画先を裏画面に設定する
    SetDrawScreen(DX_SCREEN_BACK);

    // マップの情報
    const int MAP_WIDTH = 3; // マップの幅
    const int MAP_HEIGHT = 3; // マップの高さ
    const int MAP_DRAW_WIDTH = 100; // マップ1マスの描画幅
    const int MAP_DRAW_HEIGHT = 80; // マップ1マスの描画高さ
    const int MAP_DRAW_OFFSET_X = 350; // マップの横方向の描画オフセット
    const int MAP_DRAW_OFFSET_Y = 220; // マップの縦方向の描画オフセット
    const int CHARA_DRAW_OFFSET_X = 25; // マップのマスに対するプレイヤーの横方向の描画オフセット
    const int CHARA_DRAW_OFFSET_Y = 7; // マップのマスに対するプレイヤーの縦方向の描画オフセット

    int playerX = 0; // プレイヤーのマップ上のX座標
    int playerY = 0; // プレイヤーのマップ上のY座標

    // プレイヤーの画像を読み込む
    int gh[12];
    LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh);

    // マスの描画に使う色
    int masuColor = GetColor(255, 255, 255);

    // 画面の切り替え、メッセージ処理、描画先画面の初期化、キー情報の更新
    while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && gpUpdateKey() == 0) {

        // ----------------------- 更新処理 -----------------------

        // カーソルキーの右が押されている
        if (Key[KEY_INPUT_RIGHT] == 1) {
            // 右に動けるなら動く
            if (playerX + 1 < MAP_WIDTH) playerX++;
        }

        // カーソルキーの上が押されている
        if (Key[KEY_INPUT_UP] == 1) {
            // 上に動けるなら動く
            if (playerY > 0) playerY--;
        }

        // カーソルキーの左が押されている
        if (Key[KEY_INPUT_LEFT] == 1){
            // 左に動けるなら動く
            if (playerX > 0) playerX--;
        }

        // カーソルキーの下が押されている
        if (Key[KEY_INPUT_DOWN] == 1) {
            // 下に動けるなら動く
            if (playerY + 1 < MAP_HEIGHT) playerY++;
        }

        // ----------------------- 描画処理 -----------------------

        // マスを描画する
        for (int y = 0; y < MAP_HEIGHT; y++) {
            for (int x = 0; x < MAP_WIDTH; x++) {
                // マスの座標
                int masuX = MAP_DRAW_OFFSET_X + MAP_DRAW_WIDTH * x;
                int masuY = MAP_DRAW_OFFSET_Y + MAP_DRAW_HEIGHT * y;
                // 四角形の辺を描画する
                DrawLine(masuX, masuY, masuX + MAP_DRAW_WIDTH, masuY, masuColor);
                DrawLine(masuX + MAP_DRAW_WIDTH, masuY, masuX + MAP_DRAW_WIDTH, masuY + MAP_DRAW_HEIGHT, masuColor);
                DrawLine(masuX + MAP_DRAW_WIDTH, masuY + MAP_DRAW_HEIGHT, masuX, masuY + MAP_DRAW_HEIGHT, masuColor);
                DrawLine(masuX, masuY + MAP_DRAW_HEIGHT, masuX, masuY, masuColor);
            }
        }

        // プレイヤーを描画する
        int playerDrawX = // プレイヤーを描画するX座標
            MAP_DRAW_OFFSET_X +        // マップ全体のオフセット
            MAP_DRAW_WIDTH * playerX + // マップの何マス目に描画するか
            CHARA_DRAW_OFFSET_X;       // マスに対するオフセット
        int playerDrawY = // プレイヤーを描画するY座標 (計算はX座標と同様)
            MAP_DRAW_OFFSET_Y +
            MAP_DRAW_HEIGHT * playerY +
            CHARA_DRAW_OFFSET_Y;
        DrawGraph(playerDrawX, playerDrawY, gh[8], FALSE);// プレイヤーの画像を描画
    }

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

    return 0; // ソフトの終了 

}
オフトピック
複数の質問を並行でするのは私は悪いことだとは思いませんが、
前の質問での返事も忘れないようにしてくださいね。
(念のため)
添付ファイル
charall.png
テスト用画像
charall.png (3.34 KiB) 閲覧数: 3244 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#3

投稿記事 by みけCAT » 4ヶ月前

別のバージョンも書いてみました。

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey() {
    char tmpKey[256]; // 現在のキーの入力状態を格納する
    GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
    for (int i = 0; i < 256; i++) {
        if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら
            Key[i]++;         // 加算
        }
        else {                // 押されていなければ
            Key[i] = 0;       // 0にする
        }
    }
    return 0;
}

// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    SetGraphMode(1300, 680, 32); // ウィンドウの大きさを指定
    ChangeWindowMode(TRUE);

    if (DxLib_Init() == -1) // DXライブラリ初期化処理
    {
        return -1; // エラーが起きたら直ちに終了
    }
    // 描画先を裏画面に設定する
    SetDrawScreen(DX_SCREEN_BACK);

    // マップの大きさ情報
    const int MAP_WIDTH = 3; // マップの幅
    const int MAP_HEIGHT = 3; // マップの高さ
    const int MAP_DRAW_WIDTH = 100; // マップ1マスの描画幅
    const int MAP_DRAW_HEIGHT = 80; // マップ1マスの描画高さ
    const int MAP_DRAW_OFFSET_X = 350; // マップの横方向の描画オフセット
    const int MAP_DRAW_OFFSET_Y = 220; // マップの縦方向の描画オフセット
    const int CHARA_DRAW_OFFSET_X = 25; // マップのマスに対するプレイヤーの横方向の描画オフセット
    const int CHARA_DRAW_OFFSET_Y = 7; // マップのマスに対するプレイヤーの縦方向の描画オフセット

    // マップの各マスの状態を表す定数を定義
    enum MapStatus {
        MAP_BLANK, // 空
        MAP_CHARA // プレイヤー(のみ)がいる
    };

    // マップの各マスの状態
    int pos[MAP_HEIGHT][MAP_WIDTH] = {
        {MAP_CHARA, MAP_BLANK, MAP_BLANK},
        {MAP_BLANK, MAP_BLANK, MAP_BLANK},
        {MAP_BLANK, MAP_BLANK, MAP_BLANK}
    };

    // プレイヤーの画像を読み込む
    int gh[12];
    LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh);

    // マスの描画に使う色
    int masuColor = GetColor(255, 255, 255);

    // 画面の切り替え、メッセージ処理、描画先画面の初期化、キー情報の更新
    while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && gpUpdateKey() == 0) {

        // ----------------------- 更新処理 -----------------------

        // カーソルキーの右が押されている
        if (Key[KEY_INPUT_RIGHT] == 1) {
            // 左にいるキャラを右に引っ張ってくる
            for (int y = 0; y < MAP_HEIGHT; y++) {
                // 右から先に処理する (逆にすると二重に処理される)
                // 左端は処理しない (その左が無いため)
                for (int x = MAP_WIDTH - 1; x > 0; x--) {
                    // このマスが空白なら
                    if (pos[y][x] == MAP_BLANK) {
                        pos[y][x] = pos[y][x - 1]; // 左のマスの状態を持ってくる
                        pos[y][x - 1] = MAP_BLANK; // 左のマスを空白にする
                    }
                }
            }
        }

        // カーソルキーの上が押されている
        if (Key[KEY_INPUT_UP] == 1) {
            // 下にいるキャラを上に引っ張ってくる
            for (int x = 0; x < MAP_WIDTH; x++) {
                // 上から先に処理する (逆にすると二重に処理される)
                // 下端は処理しない (その下が無いため)
                for (int y = 0; y + 1 < MAP_HEIGHT; y++) {
                    // このマスが空白なら
                    if (pos[y][x] == MAP_BLANK) {
                        pos[y][x] = pos[y + 1][x]; // 下のマスの状態を持ってくる
                        pos[y + 1][x] = MAP_BLANK; // 下のマスを空白にする
                    }
                }
            }
        }

        // カーソルキーの左が押されている
        if (Key[KEY_INPUT_LEFT] == 1){
            // 右にいるキャラを左に引っ張ってくる
            for (int y = 0; y < MAP_HEIGHT; y++) {
                // 左から先に処理する (逆にすると二重に処理される)
                // 右端は処理しない (その右が無いため)
                for (int x = 0; x + 1 < MAP_WIDTH; x++) {
                    // このマスが空白なら
                    if (pos[y][x] == MAP_BLANK) {
                        pos[y][x] = pos[y][x + 1]; // 右のマスの状態を持ってくる
                        pos[y][x + 1] = MAP_BLANK; // 右のマスを空白にする
                    }
                }
            }
        }

        // カーソルキーの下が押されている
        if (Key[KEY_INPUT_DOWN] == 1) {
            // 上にいるキャラを下に引っ張ってくる
            for (int x = 0; x < MAP_WIDTH; x++) {
                // 下から先に処理する (逆にすると二重に処理される)
                // 上端は処理しない (その上が無いため)
                for (int y = MAP_HEIGHT - 1; y > 0; y--) {
                    // このマスが空白なら
                    if (pos[y][x] == MAP_BLANK) {
                        pos[y][x] = pos[y - 1][x]; // 上のマスの状態を持ってくる
                        pos[y - 1][x] = MAP_BLANK; // 上のマスを空白にする
                    }
                }
            }
        }

        // ----------------------- 描画処理 -----------------------

        for (int y = 0; y < MAP_HEIGHT; y++) {
            for (int x = 0; x < MAP_WIDTH; x++) {

                // ----------- マスを描画する -----------

                // マスの座標
                int masuX = MAP_DRAW_OFFSET_X + MAP_DRAW_WIDTH * x;
                int masuY = MAP_DRAW_OFFSET_Y + MAP_DRAW_HEIGHT * y;
                // 四角形の辺を描画する
                DrawLine(masuX, masuY, masuX + MAP_DRAW_WIDTH, masuY, masuColor);
                DrawLine(masuX + MAP_DRAW_WIDTH, masuY, masuX + MAP_DRAW_WIDTH, masuY + MAP_DRAW_HEIGHT, masuColor);
                DrawLine(masuX + MAP_DRAW_WIDTH, masuY + MAP_DRAW_HEIGHT, masuX, masuY + MAP_DRAW_HEIGHT, masuColor);
                DrawLine(masuX, masuY + MAP_DRAW_HEIGHT, masuX, masuY, masuColor);

                // ----------- プレイヤー(など)を描画する -----------

                switch (pos[y][x]) {
                case MAP_BLANK:
                    // 何もしない
                    break;
                case MAP_CHARA:
                    // プレイヤーの画像を描画する
                    DrawGraph(masuX + CHARA_DRAW_OFFSET_X, masuY + CHARA_DRAW_OFFSET_Y, gh[8], FALSE);
                    break;
                }
            }
        }
    }

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

    return 0; // ソフトの終了 

}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#4

投稿記事 by Ouxiy » 4ヶ月前

どうもありがとうございます。
これを基に勉強してみます。いつもいつもみけCATさんにはお世話になっています。
ありがとうございます。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#5

投稿記事 by Ouxiy » 4ヶ月前

// カーソルキーの右が押されている
if (Key[KEY_INPUT_RIGHT] == 1) {
// 右に動けるなら動く
if (playerX + 1 < MAP_WIDTH) playerX++;
// 画面に出力
ScreenFlip();
// プレイヤーの画像を描画
while(playerX++){
DrawGraph(playerX, 0, gh[11], FALSE); }
}
としてみたところ、キャラが消えるのはわかるのですが、ステージも消えてしまうのはなぜでしょうか。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#6

投稿記事 by Ouxiy » 4ヶ月前

先ほどの返信はあることを行いたいために書きました。

[★行いたいこと]
静止状態と移動状態では常に真正面を向いていましたが、移動する一緒んだけ横向きにして、移動し終わったら真正面に戻るようにしたいと思い、みけさんの描いた後のプログラムに自分のアイディアを入れてみました。(うまく機能していないですが)


そこで次のように#5のコードを少し編集しました。
↓少し編集したというかみけさんと私のプログラムを組み合わせてできたコードです
// カーソルキーの右が押されている
if (Key[KEY_INPUT_RIGHT] == 1) {
// 右に動けるなら動く
if (playerX + 1 < MAP_WIDTH) playerX++;
// 画面に出力
ScreenFlip();
// 画面をクリア
ClearDrawScreen();
// プレイヤーの画像を描画

DrawGraph(playerX, playerY, gh[2], true);
}

else { while (DrawGraph(playerX, playerY, gh[8], true)); }
と以前の私の書いたプログラムと理解した上で組み込んでみたのですが、うまく機能しません。
なぜでしょうか。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#7

投稿記事 by Ouxiy » 4ヶ月前

もう一つお聞きしたいのですが、キャラ移動を
//1. 3x3マスの2次元配列
int pos[][3] = {
{0, 0, 0 },
{0, 0, 0 },
{0, 0, 0 }
};
などのような描画的に書き込んでキャラの移動を作れないかと思いました。
みけさんから配列を用いて作ったのは頂きました。それとは違い、面倒ですが、
{0, 0, 0 },
{0, 0, 0 },
{0, 0, 0 }のような表現方法でキャラ移動ができるかなと考えました。私は面倒ですができると考えているのですが、みけさんはできると思いますか?書くとするならば、移動する場合のずべてをすべて0と{}で書く必要があるのでコードは長いし、おっそいと思いますが、好奇心で質問しました。
お願いいたします。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#8

投稿記事 by Ouxiy » 4ヶ月前

毎回すいません。
コードをこんな風に編集しました、そこで問題なのですが、移動の値の入った変数を描画するための関数の引数としたいのですが、
if (playerX + 1 < MAP_WIDTH) playerX++;
playerX = playerX + 1;で正しく、+1された値がplayerXに入ったか不安です。
またDrawGraph(playerX, playerY, gh[8], true)はYの座標も扱わないとならず純粋にXの移動中の座標だけを渡せる関数はないでしょうか。
移動中の値が変化するを引数として描画できる関数がほしい。純粋にXの移動後の座標を引数にして描画してくれる関数がほしいです。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#9

投稿記事 by Ouxiy » 4ヶ月前

あるいは、キャラの移動後の座標と移動中の描画をDrawGraphでまとめても良いかもしれません。

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#10

投稿記事 by かずま » 4ヶ月前

DxLib を使ったプログラムを全く理解していませんね。

1秒間に 60枚のフレームを描画するんですよ。
ProcessMessage を呼び出すと、次のフレームの描画時刻まで待たされてから
ProcessMessage の処理が終わり戻ってきます。
ClearDrawScreen や ScreenFlip をループ内のあちこちに書いてはいけません。
描画前に ClearDrawScreen を 1回、
裏画面への描画が完了してから、ScreenFlip を 1回呼び出すだけです。

マップは back.png に書かれているものとして、こんなコードを書いてみました。

コード:

#include "DxLib.h"

void gpUpdateKey(int key[])    // キーの入力状態を更新する
{
	char tmpKey[256];             // 現在のキーの入力状態
	GetHitKeyStateAll(tmpKey);    // 全てのキーの入力状態を取得
	for (int i = 0; i < 256; i++)
		if (tmpKey[i]) key[i]++;  // フレーム数のカウントアップ
		else key[i] = 0;          // フレーム数のリセット
}

int WINAPI WinMain(HINSTANCE hi, HINSTANCE hp, LPSTR cl, int cs)
{
	SetGraphMode(1300, 680, 32); // ウィンドウの大きさを指定
	ChangeWindowMode(TRUE);

	if (DxLib_Init() == -1) return -1;

	SetDrawScreen(DX_SCREEN_BACK); // 描画先を裏画面に設定する

	const int DX = 100, DY = 80;
	const int LEFT = 350, RIGHT = LEFT + DX * 3;
	const int TOP = 220,  BOTTOM = TOP + DY * 3;

	int key[256];        // キーが押されているフレーム数を格納する
	int gh[12]; //グラフィックハンドル格納用配列
				// 0:正面, 1:右向き, 2:左向き, 3:上向き, 4:下向き, ...
	if (LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh)) goto end;
	int pgh = gh[0];  // プレイヤーのグラフィックハンドルを正面に

	int back = LoadGraph("back.png");
	if (back == -1) goto end;

	int x = LEFT, y = TOP;
	int move = 0;

	while (ProcessMessage() == 0) {
		ClearDrawScreen();  // 裏画面をクリア

		gpUpdateKey(key);   // キーの入力状態を更新

		// ---- プレイヤーの位置と移動状態の更新処理 ----
		if (key[KEY_INPUT_RIGHT] == 1  &&  x + DX < RIGHT) {
			pgh = gh[1];  // 右向きに
			move = 1;     // 移動開始
		}
		else if (key[KEY_INPUT_LEFT] == 1  &&  x - DX >= LEFT) {
			pgh = gh[2];  // 左向きに
			move = 1;     // 移動開始
		}
		else if (key[KEY_INPUT_UP] == 1  &&  y - DY >= TOP) {
			pgh = gh[3];  // 上向きに
			move = 1;     // 移動開始
		}
		else if (key[KEY_INPUT_DOWN] == 1  &&  y + DY < BOTTOM) {
			pgh = gh[4];  // 下向きに
			move = 1;     // 移動開始
		}

		if (move > 0) {   // 移動中なら
			if (++move == 10) {      // 10/60秒 経ったら
				if (pgh == gh[1]) x += DX;      // 右向きなら右移動
				else if (pgh == gh[2]) x -= DX; // 左向きなら左移動
				else if (pgh == gh[3]) y -= DY; // 上向きなら上移動
				else if (pgh == gh[4]) y += DY; // 下向きなら下移動
			}
			else if (move >= 20) {   // 20/60秒 経ったら
				pgh = gh[0];           // 正面向きに戻す
				move = 0;              // 移動終了
			}
		}

		// ---- 画面の更新処理 ----
		DrawGraph(0, 0, back, FALSE);  // 裏画面に背景を描画
		DrawGraph(x, y, pgh, FALSE);   // 裏画面にプレイヤーを描画

		ScreenFlip();  // 裏画面を表画面に切り替え
	}
end:
	DxLib_End();
	return 0;
}
質問です。
このコードはあなたの希望する動きをしますか?
このコードで理解できないところはどこですか?

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#11

投稿記事 by かずま » 4ヶ月前

かずま さんが書きました:
4ヶ月前
マップは back.png に書かれているものとして、こんなコードを書いてみました。
右キーが押されたら右向きの画像になり、10/60秒後に右に瞬間移動し、
20/60秒後に正面を向くようにしましたが、
右キーが押されたら右向きの画像になり、20/60秒経つまで、
1/60秒後ごとに少しずつ右に移動するようにもできますね。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#12

投稿記事 by Ouxiy » 4ヶ月前

質問です。
このコードはあなたの希望する動きをしますか?
このコードで理解できないところはどこですか?
ありがとうございます。読みますのでお待ちください。
フレームか時間の経過を考慮する関数で解決できないかと考えていましたが、
フレームの扱いや組み込み方がわからず、詰まっていました。

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#13

投稿記事 by みけCAT » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
// カーソルキーの右が押されている
if (Key[KEY_INPUT_RIGHT] == 1) {
// 右に動けるなら動く
if (playerX + 1 < MAP_WIDTH) playerX++;
// 画面に出力
ScreenFlip();
// プレイヤーの画像を描画
while(playerX++){
DrawGraph(playerX, 0, gh[11], FALSE); }
}
としてみたところ、キャラが消えるのはわかるのですが、ステージも消えてしまうのはなぜでしょうか。

コード:

if (playerX + 1 < MAP_WIDTH) playerX++;
によってplayerXが正になるので、

コード:

			while(playerX++){
				DrawGraph(playerX, 0, gh[11], FALSE); }
で無限ループに近い状態になり、ステージを描画する処理にしばらく行かなくなるからですね。
Ouxiy さんが書きました:
4ヶ月前
↓少し編集したというかみけさんと私のプログラムを組み合わせてできたコードです
// カーソルキーの右が押されている
if (Key[KEY_INPUT_RIGHT] == 1) {
// 右に動けるなら動く
if (playerX + 1 < MAP_WIDTH) playerX++;
// 画面に出力
ScreenFlip();
// 画面をクリア
ClearDrawScreen();
// プレイヤーの画像を描画

DrawGraph(playerX, playerY, gh[2], true);
}

else { while (DrawGraph(playerX, playerY, gh[8], true)); }
と以前の私の書いたプログラムと理解した上で組み込んでみたのですが、うまく機能しません。
なぜでしょうか。
コードがやりたいことにあっていないからですね。
playerXやplayerYの意味を私のコードから変えていないとすれば、
これらは描画する座標ではなくマップ上の位置を表すので、
そのまま描画する座標として使ってもうまくいきません。
Ouxiy さんが書きました:
4ヶ月前
もう一つお聞きしたいのですが、キャラ移動を
//1. 3x3マスの2次元配列
int pos[][3] = {
{0, 0, 0 },
{0, 0, 0 },
{0, 0, 0 }
};
などのような描画的に書き込んでキャラの移動を作れないかと思いました。
みけさんから配列を用いて作ったのは頂きました。それとは違い、面倒ですが、
{0, 0, 0 },
{0, 0, 0 },
{0, 0, 0 }のような表現方法でキャラ移動ができるかなと考えました。私は面倒ですができると考えているのですが、みけさんはできると思いますか?書くとするならば、移動する場合のずべてをすべて0と{}で書く必要があるのでコードは長いし、おっそいと思いますが、好奇心で質問しました。
お願いいたします。
申し訳ないですが、意味がよくわかりません。
しかし、C++はチューリング完全なので、チューリングマシンでできることは理論上全てできます。
(実際はメモリの容量や実行時間の制約によりできないことも出てきますが)
Ouxiy さんが書きました:
4ヶ月前
またDrawGraph(playerX, playerY, gh[8], true)はYの座標も扱わないとならず純粋にXの移動中の座標だけを渡せる関数はないでしょうか。
移動中の値が変化するを引数として描画できる関数がほしい。純粋にXの移動後の座標を引数にして描画してくれる関数がほしいです。
ほしいなら作ればいいのではないでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#14

投稿記事 by みけCAT » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
[★行いたいこと]
静止状態と移動状態では常に真正面を向いていましたが、移動する一緒んだけ横向きにして、移動し終わったら真正面に戻るようにしたいと思い、みけさんの描いた後のプログラムに自分のアイディアを入れてみました。(うまく機能していないですが)
「移動する一緒ん」というのは、「移動する一瞬(1フレーム)」のことでしょうか?
だとすると、例えばこのようにするとできるでしょう。
(「横向き」というのはよくわかりませんが、gh[8]の代わりにgh[2]を描画すれば「横向き」になると仮定しました)

コード:

#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey() {
    char tmpKey[256]; // 現在のキーの入力状態を格納する
    GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
    for (int i = 0; i < 256; i++) {
        if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら
            Key[i]++;         // 加算
        }
        else {                // 押されていなければ
            Key[i] = 0;       // 0にする
        }
    }
    return 0;
}

// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    SetGraphMode(1300, 680, 32); // ウィンドウの大きさを指定
    ChangeWindowMode(TRUE);

    if (DxLib_Init() == -1) // DXライブラリ初期化処理
    {
        return -1; // エラーが起きたら直ちに終了
    }
    // 描画先を裏画面に設定する
    SetDrawScreen(DX_SCREEN_BACK);

    // マップの情報
    const int MAP_WIDTH = 3; // マップの幅
    const int MAP_HEIGHT = 3; // マップの高さ
    const int MAP_DRAW_WIDTH = 100; // マップ1マスの描画幅
    const int MAP_DRAW_HEIGHT = 80; // マップ1マスの描画高さ
    const int MAP_DRAW_OFFSET_X = 350; // マップの横方向の描画オフセット
    const int MAP_DRAW_OFFSET_Y = 220; // マップの縦方向の描画オフセット
    const int CHARA_DRAW_OFFSET_X = 25; // マップのマスに対するプレイヤーの横方向の描画オフセット
    const int CHARA_DRAW_OFFSET_Y = 7; // マップのマスに対するプレイヤーの縦方向の描画オフセット

    int playerX = 0; // プレイヤーのマップ上のX座標
    int playerY = 0; // プレイヤーのマップ上のY座標

    // プレイヤーの画像を読み込む
    int gh[12];
    LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh);

    // 移動の向きを表す定数
    const int CHAR_MOVE_NONE = 0;
    const int CHAR_MOVE_LEFT = 1;
    const int CHAR_MOVE_RIGHT = 2;
    const int CHAR_MOVE_UP = 4;
    const int CHAR_MOVE_DOWN = 8;

    // 移動の向きに対応するプレイヤーの画像
    int gh_by_dir[16];
    for (int i = 0; i < 16; i++) gh_by_dir[i] = gh[8]; // 移動なし
    gh_by_dir[CHAR_MOVE_LEFT                  ] = gh[2]; // 左
    gh_by_dir[CHAR_MOVE_LEFT  | CHAR_MOVE_UP  ] = gh[2]; // 左上
    gh_by_dir[                  CHAR_MOVE_UP  ] = gh[2]; // 上
    gh_by_dir[CHAR_MOVE_RIGHT | CHAR_MOVE_UP  ] = gh[2]; // 右上
    gh_by_dir[CHAR_MOVE_RIGHT                 ] = gh[2]; // 右
    gh_by_dir[CHAR_MOVE_RIGHT | CHAR_MOVE_DOWN] = gh[2]; // 右下
    gh_by_dir[                  CHAR_MOVE_DOWN] = gh[2]; // 下
    gh_by_dir[CHAR_MOVE_LEFT  | CHAR_MOVE_DOWN] = gh[2]; // 左下

    // マスの描画に使う色
    int masuColor = GetColor(255, 255, 255);

    // 画面の切り替え、メッセージ処理、描画先画面の初期化、キー情報の更新
    while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && gpUpdateKey() == 0) {

        // ----------------------- 更新処理 -----------------------

        // 移動前のプレイヤーの位置を覚えておく
        int prev_playerX = playerX, prev_playerY = playerY;

        // カーソルキーの右が押されている
        if (Key[KEY_INPUT_RIGHT] == 1) {
            // 右に動けるなら動く
            if (playerX + 1 < MAP_WIDTH) playerX++;
        }

        // カーソルキーの上が押されている
        if (Key[KEY_INPUT_UP] == 1) {
            // 上に動けるなら動く
            if (playerY > 0) playerY--;
        }

        // カーソルキーの左が押されている
        if (Key[KEY_INPUT_LEFT] == 1){
            // 左に動けるなら動く
            if (playerX > 0) playerX--;
        }

        // カーソルキーの下が押されている
        if (Key[KEY_INPUT_DOWN] == 1) {
            // 下に動けるなら動く
            if (playerY + 1 < MAP_HEIGHT) playerY++;
        }

        // 移動の状況をまとめる
        int dx = playerX - prev_playerX; // 右に何マス動いたか
        int dy = playerY - prev_playerY; // 下に何マス動いたか
        int char_move = CHAR_MOVE_NONE;
        if (dx < 0) char_move |= CHAR_MOVE_LEFT;
        if (dx > 0) char_move |= CHAR_MOVE_RIGHT;
        if (dy < 0) char_move |= CHAR_MOVE_UP;
        if (dy > 0) char_move |= CHAR_MOVE_DOWN;

        // ----------------------- 描画処理 -----------------------

        // マスを描画する
        for (int y = 0; y < MAP_HEIGHT; y++) {
            for (int x = 0; x < MAP_WIDTH; x++) {
                // マスの座標
                int masuX = MAP_DRAW_OFFSET_X + MAP_DRAW_WIDTH * x;
                int masuY = MAP_DRAW_OFFSET_Y + MAP_DRAW_HEIGHT * y;
                // 四角形の辺を描画する
                DrawLine(masuX, masuY, masuX + MAP_DRAW_WIDTH, masuY, masuColor);
                DrawLine(masuX + MAP_DRAW_WIDTH, masuY, masuX + MAP_DRAW_WIDTH, masuY + MAP_DRAW_HEIGHT, masuColor);
                DrawLine(masuX + MAP_DRAW_WIDTH, masuY + MAP_DRAW_HEIGHT, masuX, masuY + MAP_DRAW_HEIGHT, masuColor);
                DrawLine(masuX, masuY + MAP_DRAW_HEIGHT, masuX, masuY, masuColor);
            }
        }

        // プレイヤーを描画する
        int playerDrawX = // プレイヤーを描画するX座標
            MAP_DRAW_OFFSET_X +        // マップ全体のオフセット
            MAP_DRAW_WIDTH * playerX + // マップの何マス目に描画するか
            CHARA_DRAW_OFFSET_X;       // マスに対するオフセット
        int playerDrawY = // プレイヤーを描画するY座標 (計算はX座標と同様)
            MAP_DRAW_OFFSET_Y +
            MAP_DRAW_HEIGHT * playerY +
            CHARA_DRAW_OFFSET_Y;
        DrawGraph(playerDrawX, playerDrawY, gh_by_dir[char_move], FALSE);// プレイヤーの画像を描画

        // FPSを落として見やすくする
        //Sleep(100);
    }

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

    return 0; // ソフトの終了 

}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#15

投稿記事 by Ouxiy » 4ヶ月前

行いたいことは書けていると思います、
ですが、goto文に赤い線の部分が出るのですが、修復の仕方がわからなかったためコードは実行していません。
画像を載せたかったのですが、行い方がわからずすいません。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#16

投稿記事 by Ouxiy » 4ヶ月前

みけCATさんいつもありがとうございます。
自分のできなかった部分を補うため 読ませて頂きます。

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#17

投稿記事 by かずま » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
ですが、goto文に赤い線の部分が出るのですが、修復の仕方がわからなかったためコードは実行していません。
もっと具体的に報告してください。

・goto に赤線が引かれるのか。
・end に赤線が引かれるのか。
・ビルドしたときにどんなエラーメッセージが表示されるのか。

コードはコピペしましたか?
手で書き写したのではありませんか?

修復方法1
・2個の goto end; を return -1; に変更し、ラベル end: を削除する。

修復方法2
if によるエラーチェックをやめて、
・LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh); だけにする。
・if (back == -1) goto end; を削除する。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#18

投稿記事 by Ouxiy » 4ヶ月前

遅くなりすいません、修復してもう一度やってみます。
修復方法ありがとうございます。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#19

投稿記事 by Ouxiy » 4ヶ月前

お陰様で実行できました。本当にありがとうございます。
あの修復に関してお聞きしたいのですが、
どうやって実行できるように修復する以下の二つの方法が見いだせたのでしょうか?
修復方法1
・2個の goto end; を return -1; に変更し、ラベル end: を削除する。

修復方法2
if によるエラーチェックをやめて、
・LoadDivGraph("charall.png", 12, 3, 4, 49, 66, gh); だけにする。
・if (back == -1) goto end; を削除する。

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#20

投稿記事 by かずま » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
あの修復に関してお聞きしたいのですが、
どうやって実行できるように修復する以下の二つの方法が見いだせたのでしょうか?
逆なんです。

最初、エラーチェックなしで書いたら、back.png の置き場所が
間違っていて、背景をロードできず、表示されませんでした。

そこで、エラーチェックがあったほうがいいと考えて、if を入れて return -1;
にしたんですが、DxLib_Init が成功しているのだから、DxLib_End を実行して
綺麗に終了させたいと考え直して、goto で DxLib_End(); に飛ばすことにした
んです。

VC++ では、これでエラーにならなかったので、それを投稿しました。
Ouxiy さんのコンパイラは何ですか?
エラーが出たのならエラーメッセージをと、お願いしたんですが、応じて
もらえないのですか?

goto で int pgh = gh[0]; や int x = LEFT; などの宣言を飛び越しています。
すると、飛び越し先で pgh や x が参照可能であるにもかかわらず、初期化が
実行されてないので、C++コンパイラが文句を言うのも納得できるんです。


動いたプログラムも希望通りのものだったんでしょうか?
移動時間を 20フレームにしましたが、それで良かったんでしょうか?

時間の経過はフレーム数をカウントすればよいので、特別な関数は不要です。
60フレームで 1秒、60秒で 1分、60分で 1時間だから、フレーム数を数える
だけで、時間の管理ができます。

今回は move という変数で移動開始後のフレーム数をカウントするようにして、
プレイヤーの向きを変える、移動する、正面を向くという表示を適切な時刻に
行わせました。
コードを読んで、そういうことを理解されましたか?

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#21

投稿記事 by Ouxiy » 4ヶ月前

あのカズマさんに質問です。
if (key[KEY_INPUT_RIGHT] == 1 && x + DX < RIGHT)に関して、右のキーが押されたら1進み、かつ、、、x + DX < RIGHTの部分が何を表しているのですか?
またx + DX < RIGHTにより右方向への移動を制限しているのでしょうか?

もう一つ、
void gpUpdateKey(int key[]) // キーの入力状態を更新する
{
char tmpKey[256]; // 現在のキーの入力状態
GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を取得
for (int i = 0; i < 256; i++)
if (tmpKey) key++; // フレーム数のカウントアップ
else key = 0; // フレーム数のリセット
}に関して、関数void gpUpdateKeyは{}より256のキーをchar型で収納しましたが、そのあとGetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を取得
for (int i = 0; i < 256; i++)としているのはなぜですか、リファレンスを呼んだのですが、専門用語が難しくイメージできませんでした。フレームや押下状態など出てきますが、どの関数がフレームや押下状態と関係があるのか、GetHitKeyStateAll(tmpKey)の必要性がわかりません。for (int i = 0; i < 256; i++)に関しては常に更新して元の状態に戻すためのプログラムだとわかります。ただforの後にif (tmpKey) key++がある理由はforで選ばれた一つのキーのフレームをカウントアップするためのプログラムですか?
そして、以上のプログラムをフレーム、キー、押下状態、バッファ、カウントアップ、リセットの用語を用いて詳しく、PCの中で何が起こっているのか教えてほしいです。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#22

投稿記事 by Ouxiy » 4ヶ月前

動いたプログラムも希望通りのものだったんでしょうか?
移動時間を 20フレームにしましたが、それで良かったんでしょうか?

時間の経過はフレーム数をカウントすればよいので、特別な関数は不要です。
60フレームで 1秒、60秒で 1分、60分で 1時間だから、フレーム数を数える
だけで、時間の管理ができます。

今回は move という変数で移動開始後のフレーム数をカウントするようにして、
プレイヤーの向きを変える、移動する、正面を向くという表示を適切な時刻に
行わせました。
コードを読んで、そういうことを理解されましたか?

正直、若干振り返るのが遅かった為、自分で解読して、書き直している最中であります。
ざっくりとは理解できました、ですが、深くまではまだ理解できていないし、先ほどのようにフレーム数やカウントの概念のイメージが付きにくいため、時間がかかります。


>>動いたプログラムも希望通りのものだったんでしょうか?
移動時間を 20フレームにしましたが、それで良かったんでしょうか?
時間の経過はフレーム数をカウントすればよいので、特別な関数は不要です。
60フレームで 1秒、60秒で 1分、60分で 1時間だから、フレーム数を数える
だけで、時間の管理ができます。
多分、一つ前に送ったプログラムがフレームやカウントを理解する上で重要に思えますが、理解が出来ていないため、アウトプットは出来ません。
私のフレームへの理解が正しければ、移動時間の20フレームは長いので7フレームほどでもよいと思いました。
そこは自力で書き直します。

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#23

投稿記事 by かずま » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
if (key[KEY_INPUT_RIGHT] == 1 && x + DX < RIGHT)に関して、右のキーが押されたら1進み、
== 1 は、キーが押されているかどうかの判定です。
1進みません。
Ouxiy さんが書きました:
4ヶ月前
かつ、、、x + DX < RIGHTの部分が何を表しているのですか?
またx + DX < RIGHTにより右方向への移動を制限しているのでしょうか?
x を DX だけ進めても右端を超えないかどうかのチェックです。
Ouxiy さんが書きました:
4ヶ月前
関数void gpUpdateKeyは{}より256のキーをchar型で収納しましたが、
char tmpKey[256]; は 256個のキーの押下状態を収納するために用意しましたが、
まだ、押下状態の値は何も入っていません。
Ouxiy さんが書きました:
4ヶ月前
そのあとGetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を取得
for (int i = 0; i < 256; i++)としているのはなぜですか、
GetHitKeyStateAll(tmpKey); は、GetHitKeyStateAll を呼び出すことによって、
キーの押下状態を tmpKey に取得します。

次に、for (int i = 0; i < 256; i++)としているのは、
取得した 256個のキーの状態を一つ一つ見ていくためです。
Ouxiy さんが書きました:
4ヶ月前
リファレンスを呼んだのですが、専門用語が難しくイメージできませんでした。フレームや押下状態など出てきますが、どの関数がフレームや押下状態と関係があるのか、GetHitKeyStateAll(tmpKey)の必要性がわかりません。
GetHitKeyStateAll を呼び出さないとキーの押下状態は取得できません。
これが、GetHitKeyStateAll(tmpKey) の必要性です。

WinMain の whileループにより、1フレームに 1回 gpUpdateKey が呼び出され、
その中で GetHitKeyStateAll が呼び出されます。
Ouxiy さんが書きました:
4ヶ月前
for (int i = 0; i < 256; i++)に関しては常に更新して元の状態に戻すためのプログラムだとわかります。
元の状態って何ですか?
for (int i = 0; i < 256; i++)としているのは、取得した 256個のキーの状態を
tmpKey[ i] で、一つ一つ見ていくためです。
tmpKey[ i] には 0 か 1 しか入っていません。
Ouxiy さんが書きました:
4ヶ月前
ただforの後にif (tmpKey[ i]) key[ i]++がある理由はforで選ばれた一つのキーのフレームをカウントアップするためのプログラムですか?
tmpKey[ i] が 0 なら、key[ i] を 0 にリセットします。
tmpKey[ i] が 1 なら、key[ i] を カウントアップします。

INPUT_KEY_RIGHT のキーを押して離すと、WinMain の whileループで gpUpdateKey
が何度も呼ばれるたびに、tmpKey[INPUT_KEY_RIGHT] は、
0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, ... となります。
gpUpdateKey の中の for文の中の if文で、key[INPUT_KEY_RIGHT] は、
0, 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, ... となります。
Ouxiy さんが書きました:
4ヶ月前
そして、以上のプログラムをフレーム、キー、押下状態、バッファ、カウントアップ、リセットの用語を用いて詳しく、PCの中で何が起こっているのか教えてほしいです。
「バッファ」って何ですか?

質問にコードを書くときは、codeタグを使ってください。
そうでないと、コードが左詰めになって、読むことができません。また、
key[ i] が key となって、その後すべての半角文字が斜体になってしまいます。

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#24

投稿記事 by みけCAT » 4ヶ月前

かずま さんが書きました:
4ヶ月前
「バッファ」って何ですか?
「バッファ」は、https://dixq.net/g/02_09.htmlに書かれている
「char *KeyStateBuf : すべてのキーの押下状態を格納するバッファのポインタ」
のことでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#25

投稿記事 by Ouxiy » 4ヶ月前

そうです。
別件で一つ気なったのですが、以下のコードについて、

コード:

// キーの入力状態を更新する
int gpUpdateKey() {
  char tmpKey[256]; // 現在のキーの入力状態を格納する
  GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
  for (int i = 0; i < 256; i++) {
    if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら
      Key[i]++;     // 加算
    } else {              // 押されていなければ
      Key[i] = 0;   // 0にする
    }
  }
  return 0;
}

コード:

char tmpKey[256];
の型は

コード:

char [256]
です。
関数GetHitKeyStateAllは格納した全キーの入力状態を知るための関数ですが、なぜ

コード:

char tmpKey[256]
を引数として関数GetHitKeyStateAllは受け取れないのでしょうか?
というか、なぜ

コード:

GetHitKeyStateAll(tmpKey)

コード:

GetHitKeyStateAll(char tmpKey[256])
とできない理由が知りたいです。
そして、

コード:

 if (tmpKey[i] != 0)

コード:

tmpKey[i] 
に関して、は型のはずですが、なぜ引数に

コード:

char tmpKey[256]
と定義したものが入るのでしょうか?if文特有のものなのでしょうか?if文の引数に

コード:

char tmpKey[i]
を使うと再定義になりますが、だとしても

コード:

tmpKey[i]
がOKな理由がわかりません。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#26

投稿記事 by Ouxiy » 4ヶ月前

すいません、一つ好奇心で質問なのですが、

コード:

if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら
      Key[i]++;    
i番目のキーが押される場合を表しますが、仮に複数のキーが同時に押された場合、プログラムは複数のキーが同時に押された場合に書き直したりするのでしょうか?

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#27

投稿記事 by かずま » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
i番目のキーが押される場合を表しますが、仮に複数のキーが同時に押された場合、プログラムは複数のキーが同時に押された場合に書き直したりするのでしょうか?
Visual Studio を使っているんだったら、KEY_INPUT_RIGHT にカーソルを
置いてみてください。
#define KEY_INPUT_RIGHT (0xCD) と表示されます。
これから KEY_INPUT_RIGHT の値が 0xCD すなわち 205 だと分かります。
同様に KEY_INPUT_LEFT の値は 0xCB すなわち 203 です。

右キーだけを押すと、tmpKey[205] だけが 1 になり、それ以外の
tmpKey[0]~tmpKey[204] と tmpKey[206]~tmpKey[255] は 0 になります。
これにより、Key[205] だけがカウントアップされ、それ以外の Key[ i] は
0 にリセットされます。

左キーと右キーを同時に押すと、tmpKey[203] と tmpKey[205] が 1 になり、
それ以外の tmpKey[ i] の値は 0 になります。
これにより、Key[203] と Key[205] がカウントアップされ、それ以外の
Key[ i] は 0 にリセットされます。

カウントアップでは Key[ i] の値が書き直されます。
リセットでは Key[ i] の値が元から 0 だったら、値は変わりませんが、
同じ値の 0 に書き直しているとも言えます。

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#28

投稿記事 by かずま » 4ヶ月前

Ouxiy さんが書きました:
4ヶ月前
というか、なぜ

コード:

GetHitKeyStateAll(tmpKey)

コード:

GetHitKeyStateAll(char tmpKey[256])
とできない理由が知りたいです。
tmpKey という名前を使えるのは、その前に、
char tmpKey[256]; というのは宣言があったということです。
これは、tmpKey という名前が何であるのかを宣言しています。
これにより、tmpKey は配列型の変数であり、
その要素数は 256、要素の型は char であることが分かります。
tmpKey の型は char [256] だと言えます。

こうして配列型の変数を宣言すると、
tmpKey[0] や tmpKey[5] などとして要素を変数として使えます。
tmpKey[0] や tmpKey[5] などの型は char です。

では、[] を付けずに tmpKey だけを書くと、それは何でしょうか?
[] の付かない tmpKey は、tmpKey[0] のアドレスになります。
すなわち tmpKey の値は &tmpKey[0] です。
tmpKey の型は char * です。

tmpKey の型は char [256] だったのが、char * に変換されたということです。
配列型は、単項&演算子や sizeof演算子のオペランドである場合を除いて
常にポインタ型に変換されるのです。

次に DxLib のリファレンスページを見ると、
int GetHitKeyStateAll(char *KeyStateBuf); と書かれています。
これは GetHitKeyStateAll が関数であり、引数の型が char *、
返却値の型が int であることを宣言しています。

この関数を呼び出す時の引数は char * 型の値でなければなりません。
だから、GetHitKeyStateAll(tmpKey); と書けるのです。

GetHitKeyStateAll(char tmpKey[256]); と書いたら、
引数として、宣言を渡そうとしていることになります。
引数には、値しか渡せません。
Ouxiy さんが書きました:
4ヶ月前
そして、

コード:

 if (tmpKey[i] != 0)

コード:

tmpKey[i] 
に関して、[ i]は型のはずですが、なぜ引数に

コード:

char tmpKey[256]
と定義したものが入るのでしょうか?if文特有のものなのでしょうか?if文の引数に

コード:

char tmpKey[i]
を使うと再定義になりますが、だとしても

コード:

tmpKey[i]
がOKな理由がわかりません。
[ i] は型ではありません。添字演算子といって、
配列の要素を指定する演算子です。

if文は、「if (式) 文1 else 文2」という構文です。
if は関数ではありません。( ) の中は引数ではなく、式であり、
その値が 0 でない場合、文1 を実行します。
その値が 0 である場合、文2 を実行します。

if (tmpKey[ i] != 0) の場合、
tmpKey[ i] != 0 という式は、!=演算子により、
tmpKey[ i] の値が 0 でない時、式の値が 1 になります。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#29

投稿記事 by Ouxiy » 4ヶ月前

tmpKey という名前を使えるのは、その前に、
char tmpKey[256]; というのは宣言があったということです。
これは、tmpKey という名前が何であるのかを宣言しています。
これにより、tmpKey は配列型の変数であり、
その要素数は 256、要素の型は char であることが分かります。
tmpKey の型は char [256] だと言えます。

こうして配列型の変数を宣言すると、
tmpKey[0] や tmpKey[5] などとして要素を変数として使えます。
tmpKey[0] や tmpKey[5] などの型は char です。

では、[] を付けずに tmpKey だけを書くと、それは何でしょうか?
[] の付かない tmpKey は、tmpKey[0] のアドレスになります。
すなわち tmpKey の値は &tmpKey[0] です。
tmpKey の型は char * です。

tmpKey の型は char [256] だったのが、char * に変換されたということです。
配列型は、単項&演算子や sizeof演算子のオペランドである場合を除いて
常にポインタ型に変換されるのです。

次に DxLib のリファレンスページを見ると、
int GetHitKeyStateAll(char *KeyStateBuf); と書かれています。
これは GetHitKeyStateAll が関数であり、引数の型が char *、
返却値の型が int であることを宣言しています。

この関数を呼び出す時の引数は char * 型の値でなければなりません。
だから、GetHitKeyStateAll(tmpKey); と書けるのです。

GetHitKeyStateAll(char tmpKey[256]); と書いたら、
引数として、宣言を渡そうとしていることになります。
引数には、値しか渡せません。

ありがとうございます。カズマさん。
あの質問があります。
まず一つに関数には値しか入れられないため、

コード:

char tmpKey[256]
ではなく、

コード:

tmpKey
の値である

コード:

&tmpKey[0]
を入れましたが、(これはchar *tmpKeyとも表せますが、)何の値を表すのでしょうか。アドレスを数字を表すのでしょうか?0~255のアドレスの数字を関数の引数として

コード:

&tmpKey[0]
を渡したのか。値を渡すということはわかったのですが、

コード:

GetHitKeyStateAll(&tmpKey[0] )
は0番の

コード:

tmpKey
のアドレスなのか0~255番の

コード:

tmpKeyのアドレスを渡すのか少し混乱しています。
また、

コード:

&tmpKey[0]
がアドレスの値を表しますが、アドレス0のみtmpKey[0]を表すのか、アドレス0~255のアドレスを

コード:

tmpKey[256]
を表すのかわかりません。次に書いてあるfor文で0~255のアドレスを渡しているのかもしれませんが、

コード:

&tmpKey[0]
と書いてあり、アドレス0のみを渡していると思ったら、コメントには「全てのキーの入力状態を得る」と書いてあったため、悩んでいます。

if文の方は、式が入るため値ではなく宣言した

コード:

char tmpKey[256]

コード:

tmpKey[i]
が入るとわかりました。関数とif文などの引数は同じ宣言

コード:

char tmpKey[256]
を利用しますが、扱うものが違うため、引数での表し方が違うとわかりました。

もう二つ目は、なぜ

コード:

tmpKey
はアドレスを渡すとわかったのでしょうか。もしかしたら私の読み落としでリファレンスに書いてあるかもしれません。
どうか以上の二つ、よろしくお願いいたします。

かずま

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#30

投稿記事 by かずま » 4ヶ月前

他人が書いた文章を、自分の書いた文章と区別できるように引用してください。

[返信]ボタンの代わりに、タイトルの右の[“]ボタンを押すと全文引用される
ので、不要な部分を削除して、quoteタグの外側に自分の文章を書いてください。

C または C++ で、配列、ポインタ、関数の基礎を学習し直してください。

今のままでは、次のプログラムも理解できないのではありませんか?

コード:

#include <stdio.h>

int main()
{
	char a[5] = { 31, 41, 59, 26, 53 };

	for (int i = 0; i < 5; i++)
		printf("%p: %d\n", &a[i], a[i]);

	printf("&a[0] = %p\n", &a[0]);
	printf("a = %p\n", a);
	printf("&a = %p\n", &a);
}
&a[0] と a は全く同じで、a[0] へのポインタ(char *) です。
&a は、&a[0] と値(アドレス) は同じですが、
型は 配列 a 全体へのポインタ(char (*)[5])です。

コード:

#include <stdio.h>
#include <stdlib.h>

void getValue(char *p)
{
	for (int i = 0; i < 5; i++)
		p[i] = rand() % 100;  // *(p + i) = rand() % 100; でも同じ
              // *p++ = rand() % 100; でも同じだが、p の値が変化する
}

int main()
{
	char a[5];

	getValue(a);  // getValue(&a[0]); でも同じ
	for (int i = 0; i < 5; i++)
		printf("a[%d] = %d\n", i, a[i]);
}
それから、自分の書いた文章を他人になったつもりで何度も読み返してください。

Ouxiy
記事: 169
登録日時: 4ヶ月前

Re: ロックマンエグゼのような3*3マスを移動できるようにしたい

#31

投稿記事 by Ouxiy » 3ヶ月前

例題ありがとうございます。
ポインタをもう少し、この例題を基に勉強させて頂きます。


返信

“C言語何でも質問掲示板” へ戻る