ページ 11

龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 04:02
by mj
龍神録の10章:敵の行動制御部分についてお聞きしたいことがあります。

コード:

 
//敵の移動パターン0での移動制御
void enemy_pattern0(int i){
    if(enemy[i].cnt<60){
        enemy[i].y+=2.0;
    }
    if(enemy[i].cnt>60+240){
        enemy[i].y-=2.0;
    }
}

//敵データの登録
void enemy_enter(){
    if(stage_count==100){//ゲームが始まって100カウントで登録
        enemy[0].cnt    =0;
        enemy[0].muki   =1;
        enemy[0].flag   =1;
        enemy[0].bltime =150;
        enemy[0].hp     =1000;
        enemy[0].hp_max =enemy[0].hp;
        enemy[0].pattern=0;
        enemy[0].x      =FIELD_MAX_X/2;
        enemy[0].y      =-20;
    }
}

//敵の行動制御
void enemy_act(){
    int i;
    for(i=0;i<ENEMY_MAX;i++){
        if(enemy[0].flag==1){//その敵のフラグがオンになってたら
            enemy_pattern0(i);
            enemy[i].cnt++;
            enemy[i].img=enemy[i].muki*3+(enemy[i].cnt%18)/6;
            //敵が画面から外れたら消す
            if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y)
                enemy[i].flag=0;
        }
    }
}
この中のvoid enemy_act()のif(enemy[0].flag==1){~}で敵が現れて特定の座標まで移動後また戻っていくという
処理が行われているようですがなぜそのような動きができるのかが分かりません。具体的に言いますとif文の中を
enemy.flag=0;のフラグが立つまでループしているように思えるのですがなぜこのような動きとなっているので
しょうか。どなたかご教授お願い致します。

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 09:13
by Dixq (管理人)
> enemy.flag=0;のフラグが立つまでループしているように思えるのですが

本当にそうですか?
ブレイクポイントを貼るなどして自分で動きを確認してみましょう。
基本的にシングルスレッドのゲームプログラムは特定部分で止まって何かイベントが起こるまでループし続けるようなことはありません。
1フレームに1度必ずメインループに処理を返します。
上のコードのforで回ってるのは
for(i=0;i<ENEMY_MAX;i++){
ですよね。
敵格納用の配列要素がENEMY_MAX個あるので、敵格納用配列要素にフラグがたったものが無いか探しているのです。
フラグが立つまで待機しているわけではありません。
現にこのループの中でフラグが立つ処理はありませんよね?

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 11:35
by Tatu
if(enemy[0].flag==1)ではなく、if(enemy.flag==1)ではないでしょうか?
(注:10章のコードではそうなっています)

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 11:52
by mj
Tatuさん、ご指摘どうもありがとうございます。

if(enemy[0].flag==1)に関しましてはif(enemy.flag==1)が正しいですね。
書き間違えていました。しかし、そこはvoid enemy_enter(){~}の[敵データの登録]部分で
enemy[0].flag=1;と1を代入しているため結局やっていること自体は変わっていないと思われます。
今のところ0番目(enemy[0])の敵しか行動するフラグを立てていないはずなので。

ご指摘があった部分をif(enemy.flag==1)に修正した場合も同様の実行結果になったことを
確認しております。

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 12:28
by Tatu
mjさんは「なぜそのような動きをする理由ができるのかが分かりません」と書いていますが
mjさんはどのような動きをすると思ったのですか?

(1)ループで一気に移動するまたはプログラムが停止する
一度のメインループで一度しかenemy[0]についての処理を行わないため、
そのような動きにはなりません。
No:1でif文にループ機能があるみたいな書き方をされていますが
while文と勘違いしていませんか?

(2)ずっと下に進み続ける
enemy_pattern0(i)では
enemy.cntが60未満の時は下に進む。
enemy.cntが60から60+240の間の時は移動しない。
enemy.cntが60+240より大きい時は上に進む。
となっています。
enemy_pattern(i)の後にenemy.cntを増やしているため
いずれはenemy.cntが60以上になり、動きが変わります。

(3)その他

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 14:14
by mj

コード:

//敵の行動制御
void enemy_act(){
    int i;
    for(i=0;i<ENEMY_MAX;i++){
        if(enemy[i].flag==1){//その敵のフラグがオンになってたら
            enemy_pattern0(i);
            enemy[i].cnt++;
            enemy[i].img=enemy[i].muki*3+(enemy[i].cnt%18)/6;
            //敵が画面から外れたら消す
            if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y)
                enemy[i].flag=0;
        }
    }
}
私が上記コードで行われている動作、及び疑問に思った部分を説明致します。
とりあえず、一番最初にfor(i=0;i<ENEMY_MAX;i++)に入ったときの処理を追って行きたいと思います。

void enemy_act(){
int i;
for(i=0;i<ENEMY_MAX;i++){
          ↑
0番~29番の敵の移動制御をします。今回は0番目。

if(enemy.flag==1){
          ↑
0番目のフラグが[敵データ登録]で1(オン)となります。

enemy_pattern0(i);
          ↑
下記で行われるenemy.cnt++;が60未満(0~59)のときy座標を+2(2~120)移動
enemy.cnt++;が300より大きくなったときy座標を-2(戻る)
enemy.cnt++;が60以上300以下のときenemy.y+=0;(約4秒間その場に停止、その間アニメーション自体は続行)
初期値では[敵データ登録]よりenemy[0].cnt=0;となるのでenemy.y+=2.0;が代入される。

enemy.cnt++;
          ↑
[敵データ登録]で初期値にenemy[0].cntに0が代入されているため++で1となる。

enemy.img=enemy.muki*3+(enemy.cnt%18)/6;
          ↑
ここではenemy[i].cnt++;が1プラスされる毎に導き出される商が3~5になります。
上記でenemy[0].cnt++;で1が代入されたため、導き出される最初の商は
enemy[0].img=1*3+(1%18)/6=3+0=3となりenemy[0].imgに3が代入される。

if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y)
          ↑
ここではキャラクタがフィールド外に出てしまったときにenemy[i].flagに0を代入してフラグを終了する。
一番最初の実行ではenemy[0].yに+2が代入され[敵データ登録]よりenemy[0].yの初期値が-20であることと
合わせてenemy[0].y==-18となっているため[enemy[i].flag=0;]の条件は成立しません。よって、if文は飛ばされます。
今のところenemy[i].yの行動処理しか行われていないためenemy[i].xは考えなくてもいいですね。

enemy[i].flag=0;
}
          ↑
とりあえずここまでで一番最初に実行されるif文の処理が終了して次はi=1(1番目の敵)のfor文が始まるのではと
思ったのですが実際の実行結果ではenemy[i].flag=0;が成立するまで[enemy[i].cnt++;]が繰り返し実行されて
いるように感じます。
}
}

以上が私が考えているvoid enemy_act()の中で一番最初に行われている処理となります。

Tatuさんがご指摘くださった通り私が疑問に思った部分はまさにif(enemy[i].flag==1)がwhile文のような
動きをしていると思ってしまうところです。私なら、下記のようにwhile文でenemy[i].flag=0;が成立するまで
enemy[i].cnt++;をカウントすれば良いのではと考えているのこれでは敵が表示されずに上手く実行できません
でした。こちらに関しましてどの部分の考え方が間違っているのでしょうか。

コード:

void enemy_act(){
    int i;
    for(i=0;i<ENEMY_MAX;i++){
        while(enemy[i].flag==1){//その敵のフラグがオンになってたら
            enemy_pattern0(i);
            enemy[i].cnt++;
            enemy[i].img=enemy[i].muki*3+(enemy[i].cnt%18)/6;
            //敵が画面から外れたら消す
            if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y)
            {
                enemy[i].flag=0;//ここはフラグを0にしなくても抜けると思いますが
                break;
            }
        }
    }
}

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 14:32
by softya(ソフト屋)
Dixq (管理人)さんの言われるようにデバッガでプログラムの流れを追いかけての考察でしょうか?
考察も大事ですが、デバッガで流れを追ってこそ理解できることもあります。
もしデバッガで追いかける方法がわからないのであれば、まず調べる事が必要かと思います。

【補足】
それと書かれている内容を見るに、ゲームプログラミングの館の内容を理解されていないようにお見受けします。
ゲームプログラミングの館は理解されたのでしょうか?

【補足の追記】
上の質問を書いたのは、次のような疑問が書かれていたからです。

>Tatuさんがご指摘くださった通り私が疑問に思った部分はまさにif(enemy.flag==1)がwhile文のような
>動きをしていると思ってしまうところです。私なら、下記のようにwhile文でenemy.flag=0;が成立するまで
>enemy.cnt++;をカウントすれば良いのではと考えているのこれでは敵が表示されずに上手く実行できません
>でした。こちらに関しましてどの部分の考え方が間違っているのでしょうか。

もっとも理解していないと行けないのは、メインループに戻らないと画面が反映されない&DrawGraph()で画像が描画されると言う部分だと思います。
そして、その処理は毎フレーム行う必要があります。
もし、while文でループしても画面が更新されると思っている場合は、C言語の基本的なプログラムの流れが理解が出来ていないと言うことなので、それこそデバッガのステップ実行(F10)でプログラムの流れを実感してもらうが一番良いと思います。

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 15:24
by ジンセイ
自分も初めのころはネストが深くなると訳がわからなくなっていたので気持ちは良くわかります。
こういうのは理解というよりも馴れだと思います。

以下のように書くとどうでしょう?
まったく同じ処理ですが、少しはわかりやすくなるんじゃないでしょうか?

<code>
void enemy_act(){
int i;
for(i=0;i<ENEMY_MAX;i++){
if(enemy.flag!=1){//その敵のフラグがオフだったら何もしない
continue;
}

enemy_pattern0(i);
enemy.cnt++;
enemy.img=enemy.muki*3+(enemy.cnt%18)/6;

//敵が画面から外れたらフラグをオフにして消す
if(enemy.x<-50 || FIELD_MAX_X+50<enemy.x || enemy.y<-50 || FIELD_MAX_Y+50<enemy.y)
enemy.flag=0;
}
}
</code>

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 15:27
by ジンセイ
<code></code>じゃなかった

コード:

void enemy_act(){
    int i;
    for(i=0;i<ENEMY_MAX;i++){
        if(enemy[i].flag!=1){//その敵のフラグがオフだったら何もしない
            continue;
        }

        enemy_pattern0(i);
        enemy[i].cnt++;
        enemy[i].img=enemy[i].muki*3+(enemy[i].cnt%18)/6;

        //敵が画面から外れたらフラグをオフにして消す
        if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y)
            enemy[i].flag=0;
    }
}

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 17:04
by Tatu
考えのどこが間違っているのかを考える前に他の人に返信しましょう。

まずはsoftya(ソフト屋)さんにデバッグの仕方を知っているかとゲームプログラミングの館を理解したかをこたえ、
デバッグの仕方を知らない場合はやり方か調べ方を聞いてください。

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 19:12
by mj
少し目を離していた間に色々とご返信を頂いていたようで皆様申し訳ありませんでした。

>Dixq さん,softya(ソフト屋)さん
No: 6での記載はNO: 2でDixq さんにご指摘を頂いたようにプログラミングがどのように
動いているのかを自分なりに動きを追ってみたものとなります。説明が色々不足しており
分かりにくくなってしまい申し訳ありません。そのため、softya(ソフト屋)さんのいう
デバッガでプログラムの流れを追いかけての考察というものではないと思われます。
デバッガでプログラミングを流れみるという方法があること自体ここで始めて知りました。
Visual Studioにはデバッガのステップ実行(F10)という便利な機能がついていたのですね。
そして、[ゲームプログラミングの館]ですが[~入門編~],[~ゲーム基本編~],[~中級編~]まで
一通り学習したつもりですがまだ理解が不足している部分があることは否定できません。
今行っているゲーム処理の流れはenemy.cppではenemy画像を表示させる座標とその座標にいるときに
表示されるアニメーションパターン(0~9)の番号を指定、その後graph.cppで該当する座標に指定された
アニメーションパターン(0~9)を描画。そしてmain.cppで関数を呼び出して実行という流れになっていると
考えています。
プログラムが長くなると処理が複雑になっていくため自力ではプログラミングを追い切れなくなっている
感じがします。デバッガのステップ実行(F10)の機能を使い少しプログラムの流れを改めて確認しようと思います。

>ジンセイさん
アドバイスの方ありがとうございます。確かにcontiue機能を使えばenemy.flag!=1になったとき
処理をfor文に返してくれて同じ結果が得られますね。しかし、現状のコードの疑問がまだ解消して
おらず、他の方たちからも色々とアドバイスを頂いたのでもう少し問題のほうを追ってみようと思います。

>Tatu さん
ご指摘ありがとうございます。プログラミング以前に礼儀のほうがなっていませんでした。
反省しております。softya(ソフト屋)さんにステップ実行(F10)機能をつかってのデバックを
教えてもらったのでそちらのほうを試してみようと思っています。

Re: 龍神録:敵の行動制御について

Posted: 2013年2月10日(日) 19:19
by softya(ソフト屋)
>今行っているゲーム処理の流れはenemy.cppではenemy画像を表示させる座標とその座標にいるときに
>表示されるアニメーションパターン(0~9)の番号を指定、その後graph.cppで該当する座標に指定された
>アニメーションパターン(0~9)を描画。そしてmain.cppで関数を呼び出して実行という流れになっていると
>考えています。

まさに、そこの流れの理解の問題だと思います。
enemy.cppのenemy_act()でwhile(enemy.flag==1){とループした場合にgraph.cpp関連の描画処理はループを抜けるまで行われません。
デバッガでF10とブレークポイントを使って確認してみて下さいね。

Re: 龍神録:敵の行動制御について

Posted: 2013年2月11日(月) 02:24
by mj
>softya(ソフト屋)さん
ステップ実行処理とても役に立ちました。今まで地道に計算していた変数の挙動が自動的に
表示されるようになっていたのでこの機能をもっと早くに知れればよかったです。
おかげで自分がどこで誤解をしていたのかを理解することができました。

私はenemy_main();が呼び出されたときに1度に敵がフレームインしてフレームアウトするまでの
処理が行われると思っていたのですが実際には1度呼び出されただけでは敵の行動を制御する
enemy.cnt++;は+1しかされていなかったわけですね。
だから移動距離もy座標を+2とほんのわずかだけ。enemy_main();がmain.cppで何度も呼び出される
ことによってフレームインしてフレームアウトしていくという行動が作られることを理解することができました。

皆様、此度も私のような素人に時間を割いて頂きありがとうございました。おかげでプログラミングに
おける知識だけではなくプログラミングに役立つデバック機能も知ることができました。