質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • C#

    7689questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • C

    3979questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    3759questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual C++

    119questions

    Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

ドットイートゲーム 敵の移動アルゴリズム を教えてください。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,100

masuter0413

score 15

前提・実現したいこと

こんちには。私は現在パックマンのようなドットイートゲームを作成しています。
まだプログラミングを始めたばかりで、ものすごく簡単ではございますが、基本となるコードはなんとか自分で書くことができました。
しかし、次の問題点がどうしても改善できません。

発生している問題・エラーメッセージ

敵が壁に当たると全く動かなくなってしまう。

該当のソースコード

///////////////////////////////////////////////////////////////
//
// パックマンプログラム pacman2.c
//
//                                 平成29年2月9日
///////////////////////////////////////////////////////////////
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h> // system()
#include <windows.h> // Sleep()
#include <conio.h> // kbhit()

#define SIZE 15 // 一辺の長さ
#define EMPTY 0 // 何もない場所は0
#define WALL 1    // 壁
#define FOOD 2    // えさ(ドット)
#define POWER_FOOD 4 //パワーエサ
#define PACMAN 3// 主人公

int main()
{
    int wait_time = 300;
    int x, y, j;
    int food_count = 0;//エサの個数
    int power_food_count = 0;//パワーエサの個数
    int cx, cy;//パックマン
    int ex, ey;//敵
    int key;
    int point = 0;
    int kx, ky;//自機の座標を保存するための変数
    int mx, my;//敵の座標を保存するための変数
    int life = 3;//ライフ

    int field[SIZE][SIZE] =
    {
        { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
        { 1,2,2,2,2,2,2,2,2,2,2,4,2,2,1 },
        { 1,2,1,1,1,2,1,1,1,2,1,2,1,2,1 },
        { 1,2,1,0,1,2,1,0,1,2,1,2,2,2,1 },
        { 1,2,1,1,1,2,1,1,1,2,1,2,1,2,1 },
        { 1,2,2,2,2,2,2,2,2,2,2,2,2,2,1 },
        { 1,1,1,1,1,0,1,0,1,1,1,0,1,1,1 },
        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
        { 1,1,1,1,1,2,1,2,2,1,1,2,1,1,1 },
        { 1,4,2,2,2,2,1,2,1,1,1,2,2,2,1 },
        { 1,2,1,1,1,1,0,0,0,0,2,2,1,2,1 },
        { 1,2,2,2,0,2,2,1,2,1,2,0,1,2,1 },
        { 1,2,1,1,1,0,1,1,2,2,2,1,1,2,1 },
        { 1,2,2,2,2,2,2,2,2,1,2,2,2,4,1 },
        { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
    };

    cx = 7, cy = 7; //パックマンの初期位置
    ex = 1, ey = 1; //敵の初期位置

    //エサの個数を数える
    for (y = 0; y < SIZE; y++)
    {
        for (x = 0; x < SIZE; x++)
        {
            if (field[y][x] == FOOD)
                food_count++;
            else if (field[y][x] == POWER_FOOD)
                power_food_count++;
        }

    }

    while (1)
    {
        // まずはPACMANの動き
        // キーが押されていたら、キーコードを取得する
        // 4なら左、6なら右、8なら上、2なら下
        if (kbhit()) // キーが入力されていれば真
        {
            key = getch();//一文字入力、エコーバックなし
            kx = cx, ky = cy;//パックマンの座標を保存する
            switch (key)
            {
            case '4':
                cx--;
                break;
            case '6':
                cx++;
                break;
            case '2':
                cy++;
                break;
            case '8':
                cy--;
                break;
            }

            cx = (cx + SIZE) % SIZE; //ワープ            
            switch (field[cy][cx])
            {
            case WALL://壁
                cx = kx, cy = ky; //動けないので cx, cy を元に戻す
                                  //壁に当たると自機の座標に元の座標を代入する
                break;
            case FOOD:
                field[cy][cx] = 0;//餌を食べると餌が消える
                point += 10;      //得点加算
                food_count--;     //餌の数を減らしていく
                break;
            case POWER_FOOD:
                field[cy][cx] = 0; //パワーエサを食べると消える
                life += 1;         //LIFE加算
                power_food_count--;//パワーエサの数を減らしていく
                break;


            }

        }


        //ここから敵の動き

        mx = ex; my = ey; //敵の座標を保存する

            if (ex > cx)      //敵のx座標 > パックマンのx座標 なら 敵のx座標を減らしてパックマンに近づく。
                ex--;
            else if (ex < cx) //敵のx座標 < パックマンのx座標 なら 敵のx座標を増やしてパックマンに近づく。
                ex++;
            else if (ey < cy) //敵のy座標 > パックマンのy座標 なら 敵のy座標を減らしてパックマンに近づく。
                ey++;
            else if (ey > cy) //敵のy座標 < パックマンのy座標 なら 敵のy座標を増やしてパックマンに近づく。
                ey--;

            if (field[ey][ex] == WALL)  //もし敵が壁に当たったらもとの座標に戻す。
            {
                ex = mx; ey = my;

            }


        ex = (ex + SIZE) % SIZE; //ワープ

        if (cx == ex && cy == ey) {  //もしパックマンと衝突したら マップの真ん中に移動し少し待つ。
            life--;                  // そしてLIFE-1
            ex = 7, ey = 7;
            Sleep(wait_time);
        }


        // 画面表示
        for (y = 0; y < SIZE; y++) {
            for (x = 0; x < SIZE; x++) {
                if (x == cx && y == cy)
                    printf("C ");
                else if (x == ex && y == ey)
                    printf("◇");
                else if (field[y][x] == FOOD)
                    printf(". ");
                else if (field[y][x] == WALL)
                    printf("■");
                else if (field[y][x] == POWER_FOOD)
                    printf("P ");
                else
                    printf(" ");
            }
            printf("\n");
        }

        // すべての場所で field[y][x] の値を元に表示


        printf("\n");
        printf(" (cx,cy) = (%d, %d)\n", cx, cy);
        printf(" (ex,ey) = (%d, %d)\n", ex, ey);
        printf(" point: %d\n", point);
        printf("  LIFE: %d\n", life);

        if (life == 0)
        {
            printf("┏ ┏━┓┏━┓┏┳┓┳━┓ ┏━┓┳  ┳┳━┓┳━┓ ┓\n");
            printf("┃ ┃  ┓┣━┫┃┃┃┣┫   ┃  ┃┃  ┃┣┫  ┣┳┛ ┃\n");
            printf("┗ ┗━┛┻  ┻┻  ┻┻━┛ ┗━┛┗━┛┻━┛┻┗┛ ┛\n");
            Sleep(wait_time);
            break;
        }

        if (food_count == 0)       //餌を食べきったとき(=CRER)の画面表示
        {
            printf("┏ ┏━┓┏━┓┏┳┓┳━┓ ┏━┓ ┳━┓ ┳━┓ ┳━┓ ┓\n");
            printf("┃ ┃  ┓┣━┫┃┃┃┣┫   ┃     ┣┳┛ ┣┫   ┣┳┛ ┃\n");
            printf("┗ ┗━┛┻  ┻┻  ┻┻━┛ ┗━┛ ┻┗┛ ┻━┛ ┻┗┛ ┛\n");
            Sleep(wait_time);
            break;

        }
        Sleep(wait_time);// 1秒間(wait_timeミリ秒)何もしない
        system("cls");// 画面消去
    }

    return 0;
}

試したこと

敵が壁に当たった時に、一度ランダムに動かそうと思い、

if (field[ey][ex] == WALL)  //もし敵が壁に当たったらもとの座標に戻す。
{
ex = mx; ey = my;
j = rand() % 4;
switch (j)
{
case '0':
ex--;
break;
case '1':
ex++;
break;
case '2':
ey++;
break;
case '3':
ey--;
break;
}

}

にしましたが、やはり敵が壁に当たると全く動きません。

補足情報(言語/FW/ツール等のバージョンなど)

C++
Visual Studio 2015

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • crowmt

    2017/02/11 12:35

    今の作りですと、パックマンと敵の間に壁があると敵が進まない気がしますが…

    キャンセル

  • crowmt

    2017/02/11 12:41

    敵の動きで、ダイレクトにパックマンの位置を見るタイミングを、交差点にきたら行うとかにするなどどうでしょうか。

    キャンセル

回答 2

+1

とりあえずの思いつきですが下記みたいなのはどうでしょう?
全く動かさずに書いているので、考え方の参考程度にしてください。

    // int ed=0;
    // edは敵の動く方向を覚えておく変数とする。定義は適当なところで行う
    // 0:左 1:右 2:上 3:下



    //ここから敵の動き
    mx = ex; my = ey; //敵の座標を保存する
    md = ed;  //敵の動く方向を保存する

    while(1)
    {
        switch(ed)
        {
            case 0:
                ex--;
                break;
            case 1:
                ex++;
                break;
            case 2:
                ey--;
                break;
            case 3:
                ey++;
                break;
        }

        if (field[ey][ex] == WALL)  //移動先が壁の場合
        {
            //とりあえず移動元に位置を戻して
            ex = mx; ey = my;
            //次に進む方向を決定する
            switch(md)
            {
                case 0:
                    if (ed == 0)
                    {
                        //左に進んでいるときに壁に当たった場合
                        //進める方向は上か下になる
                        //そこで自機のY座標と比較して方向を決める
                        if (ey > cy)
                        {
                            ed = 2;
                        }
                        else
                        {
                            ed = 3;
                        }
                    }
                    else if (ed == 2)
                    {
                        //ここに来るということは、現在の位置の上が壁だったので下に方向を変える
                        ed = 3;
                    }
                    else if (ed == 3)
                    {
                        //ここに来るということは、現在の位置の下が壁だったので上に方向を変える
                        ed = 2;
                    }

                    break;
                case 1:
                    //0の場合を参考に
                    break;
                case 2:
                    //0の場合を参考に
                    break;
                case 3:
                    //0の場合を参考に
                    break;
            }
        }
        else
        {
            // 移動先が壁ではない場合、敵の位置を決定するループから抜ける
            break;
        }
    }

とりあえず、移動先が壁の場合の次の方向の決定方法を変更すれば、いろいろ性格がでると思います。
また、移動する前に分岐点があるかチェックして、そこを曲がるかどうかの処理を入れればいいと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

私はプログラミング初心者かつRubyしかわからない人間です。
移動先が壁かどうかを調べてから移動するというのは
どうでしょうか。
fieldの8つめの配列は
[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]ですが
こちらの都合で
[ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ]に
変更しました。
以下のプログラムでは敵が階段状の壁に当たると
引っかかります。(自機を動かすとまた動くようになります。)
移動先が壁かどうかを先に調べる以外は
質問に書いてあったことを書き換えただけなので
もしかしたらそちらの言語でも移動先が壁かどうかを
先に調べるようにすれば動くかもしれません。

#encoding : utf-8
require"dxruby"

@map = [[ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ],
        [ 1,2,2,2,2,2,2,2,2,2,2,4,2,2,1 ],
        [ 1,2,1,1,1,2,1,1,1,2,1,2,1,2,1 ],
        [ 1,2,1,0,1,2,1,0,1,2,1,2,2,2,1 ],
        [ 1,2,1,1,1,2,1,1,1,2,1,2,1,2,1 ],
        [ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,1 ],
        [ 1,1,1,1,1,0,1,0,1,1,1,0,1,1,1 ],
        [ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ],
        [ 1,1,1,1,1,2,1,2,2,1,1,2,1,1,1 ],
        [ 1,4,2,2,2,2,1,2,1,1,1,2,2,2,1 ],
        [ 1,2,1,1,1,1,0,0,0,0,2,2,1,2,1 ],
        [ 1,2,2,2,0,2,2,1,2,1,2,0,1,2,1 ],
        [ 1,2,1,1,1,0,1,1,2,2,2,1,1,2,1 ],
        [ 1,2,2,2,2,2,2,2,2,1,2,2,2,4,1 ],
        [ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ]]

@map_tile = []
@map_tile[0] = Image.new(30, 30, C_BLACK) #何もない
@map_tile[1] = Image.new(30, 30, C_WHITE) #壁
@map_tile[2] = Image.new(30, 30, C_BLACK)
@map_tile[3] = Image.new(30, 30, C_GREEN) #自機
@map_tile[4] = Image.new(30, 30, C_BLACK)
@map_tile[5] =  Image.new(30, 30, C_RED) #敵

Window.width = 450
Window.height = 450

start_count = 0

cx = 7
cy = 7

ex = 1
ey = 1

enemy_count = 0

game_overcount = 0
next_count = 0
font = Font.new(55)

Window.loop do
  #初期位置の指定
  if start_count == 0 then
    @map[cx][cy] = 3
    @map[ex][ey] = 5
    start_count = 1
  end

  #自機の操作
    if Input.keyPush?(K_W) then
      if @map[cx - 1][cy] != 1 && @map[cx - 1][cy] != 5 then
         @map[cx][cy] = 0
         @map[cx -= 1][cy] = 3
         start_count = 2
      end
    end
    if Input.keyPush?(K_S) then
      if @map[cx + 1][cy] != 1 && @map[cx + 1][cy] != 5 then
         @map[cx][cy] = 0
         @map[cx += 1][cy] = 3
         start_count = 2
      end
    end
    if Input.keyPush?(K_D) then
      if @map[cx][cy + 1] != 1 && @map[cx][cy + 1] != 5 then
         @map[cx][cy] = 0
         @map[cx][cy += 1] = 3
         start_count = 2
      end
    end
    if Input.keyPush?(K_A) then
      if @map[cx][cy - 1] != 1 && @map[cx][cy - 1] != 5 then
         @map[cx][cy] = 0
         @map[cx][cy -= 1] = 3
         start_count = 2
      end
    end


    #敵
    if start_count == 2
      if ex > cx
        if @map[ex - 1][ey] != 1 then
           @map[ex][ey] = 0
           @map[ex -= 1][ey] = 5
        end
      end
      if ex < cx
        if @map[ex + 1][ey] != 1 then
           @map[ex][ey] = 0
           @map[ex += 1][ey] = 5
        end
      end
      if ey > cy
        if @map[ex][ey - 1] != 1 then
           @map[ex][ey] = 0
           @map[ex][ey -= 1] = 5
        end
      end
      if ey < cy
        if @map[ex][ey + 1] != 1 then
           @map[ex][ey] = 0
           @map[ex][ey += 1] = 5
        end
      end
      start_count = 1
    end

  Window.draw_tile(0, 0, @map, @map_tile, 0, 0, 15, 15)

  if @map[cx][cy] == 5
     Window.draw(0, 0, Image.new(450, 450, C_RED))
     Window.draw_font(90, 150, "Game Over", font)
     next_count += 1
     if next_count >= 180 then
       break
     end
  end
end

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    7689questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • C

    3979questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    3759questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual C++

    119questions

    Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。