ページ 11

なぜこうなるのか

Posted: 2010年5月29日(土) 21:42
by ハナブトオオトカゲ
シューティングの基本の、

else if( Key[ KEY_INPUT_RETURN ] == 1 ){//5カウント以上たっていたら
counter=0; //カウンターを戻す
for(i=0;i<10;i++){
if(tama.flag==0){ //発射していない玉を探し、
tama.flag=1; //発射フラグを立てる
break;
}
}
}
for(i=0;i<10;i++){
if(tama.flag==1){ //発射している玉なら
tama.y-=8; //座標を8減らす
DrawGraph( tama.x , tama.y , image[1] , TRUE );//玉を描画
if(tama.y < -32){ //もし画面外まで来たら
tama.y=480; //初期値に戻し、
tama.flag=0; //発射フラグを戻す
}
}
}


についてなのですが、これで、エンターが1回押されただけでtama[0~9]のflagがすべて1になってしまいそうなのですが、なぜ1つ1つ弾を打つことができるのでしょうか?

全部が発射フラグになっていたら全部が一気に飛びそうですが・・・。 画像

Re:なぜこうなるのか

Posted: 2010年5月29日(土) 22:08
by シエル
ひとつフラグを立てる毎に、breakで抜けてるから。

Re:なぜこうなるのか

Posted: 2010年5月29日(土) 22:18
by ハナブトオオトカゲ
あっ

whileで戻ってきたらまた0からやり直しだ・・・と思っていたら、wlileで戻ってきたときにはまだ画面上で上に動いている途中だから、i=0は飛ばされてi=1に行くというわけですね・・・!!!

ものすごくすっきりしました。

Re:なぜこうなるのか

Posted: 2010年5月29日(土) 23:10
by メタ
質問者ではないのですが、breakすると、
if(tama.flag==0){ //発射していない玉を探し、
tama.flag=1; //発射フラグを立てる
break;
}
から抜けるのでしょうか?
それとも、その前のforやelse ifからも抜けて、main関数からも抜けるのでしょうか?

何言ってるんだこいつ・・・と思うかもしれませんが、お願いします。

Re:なぜこうなるのか

Posted: 2010年5月29日(土) 23:12
by シエル
for(i=0;i<10;i++)からだけ抜ける。

Re:なぜこうなるのか

Posted: 2010年5月29日(土) 23:18
by メタ
なるほど!ありがとうございます!そうすると理解できました!
ハナブトオオトカゲさんの発言はちょっとまだ理解できませんが。。

Re:なぜこうなるのか

Posted: 2010年5月30日(日) 12:05
by Dixq (管理人)
ではちょっと詳しく説明してみます。


全てのデータは0で初期化してあるとし、
フラグは

tama[0].flag=1;
tama[1].flag=1;
tama[3].flag=1;

であるとしましょう。


数が少ない要素から登録を行いますが、画面から外に出るとフラグをオフにするので、
時間がたつと、数が少ないものからオフになります。
従って上記のように穴があるような状態になる場合があります。
01: while(1){
02:     if(counter<5){                           //前にエンターを押してから5カウント未満なら
03:            counter++;                        //カウントアップ
04:     }
05:     else if( Key[ KEY_INPUT_RETURN ]  == 1 ){//5カウント以上たっていたら
06:         counter=0;                           //カウンターを戻す
07:         for(i=0;i<10;i++){             
08:             if(tama.flag==0){             //発射していない玉を探し、
09:                 tama.flag=1;              //発射フラグを立てる
10:                 break;                       //for文から処理を抜ける
11:             }
12:         }
13:     }
14: }

まず5カウント以上押しっぱなしになるまでは2行目のifに入ります。
5カウント以上たてば5行目のelse ifの方に入ります。
counter=0;にして、
7行目でtamaのフラグが0の物を探します。

8行目でまず、i=0において、tama・・つまりtama[0].flagが0かどうかを調べます。
しかし今、このフラグ1なので、そのまま7行目のi++が行われてi=1になります。
tama[1].flagを調べますが、これも1なので、戻ってi++が行われます。
tama[2].flagを調べるとこれが0なので、if文の中、9行目に入ります。
10行目でbreakします。
するとこのbreakは12行目のカッコから外にでます。
そのまま13行目へ差し掛かり、1行目に戻るという事です。

落下判定について

Posted: 2010年6月02日(水) 21:05
by 伊藤
モデムが壊れてネットにつなげられなく回答が遅れてしまいました。
申し訳御座いませんでした。

http://www.play21.jp/board/formz.cgi?ac ... &rln=52922
で質問させて頂いたものです。

DXライブラリを使ってゲームを作っております。
移動の判定が上手くいかず
どうしてもわからなかったので質問させて頂きました。

仕様
・プレイヤーは常に左右のどちらかに移動し続けている
・壁に当たったら移動方向を反転する
・床がなかったら落ちる
といったよくありそうなゲームです。

壁に当たった際の左右反転は出来たのですが
落ちる際に左右の移動の判定が入ってしまったり
落ちた際に壁にめり込んで急に左右反転してしまうことがあります。

必要そうな一部分だけを切り出したのでミスがあったらすみません。

Re:落下判定について

Posted: 2010年6月03日(木) 02:58
by 伊藤
やり方を変えてみたのですがやはり上手くいきません・・・。
本当にわかりません・・・。
void PlayMove(void){

    // 移動量の初期化
    Play.Mx = 0;
    Play.My = 0;
    Play.Ox = Play.Px;
    Play.Oy = Play.Py;
    Play.Cx = int(Play.Px-16 / CEL_SW);
    Play.Cy = int(Play.Py-16 / CEL_SH);

    // 移動量のセット
    for(int speed=0; speed<Play.Speed; speed++){
        Play.Mx = 1;
        if(Play.Dir) Play.Mx *= -1;
        
        for(int i=0; i<OBJ_MAX; i++){

            if((Play.Px-16)%32 == 0){
                //if(Map.Ptn == 0) Play.My = 1;
            }

            // 左右の判定
            if(Map.Ptn != 0){
                if(Play.Px-CEL_SW/2 < Map.Px+CEL_SW/2 && Play.Px+CEL_SW/2 > Map.Px-CEL_SW/2 &&
                    Play.Py-CEL_SH/2 < Map.Py+CEL_SH/2 && Play.Py+CEL_SH/2 > Map.Py-CEL_SH/2){
                        
                        /*
                        // Y座標用
                        for(int j=0; j<OBJ_MAX-CEL_W; j++){
                            if(Map.Cy != CEL_W-1 && Map.Cx == Map[j].Cx && Map.Cy+1 == Map[j].Cy &&Map[j].Ptn == 0){
                                Play.My = 1;
                                break;
                            }
                        }
                        */
                        
                        Play.Dir = !Play.Dir;
                        Play.Px = Play.Ox;
                        Play.Py = Play.Oy;
                        Play.Mx = 0;
                        Play.My = 0;
                        break;
                }
            }
            /*
            if(Play.Px%CEL_SW == 0){
                if(Play.Px == Map.Px && Play.Py == Map[i].Py+32 && Map[i].Ptn == 0){
                    Play.My = 10;
                    tf = true;
                }
            }
            */
        }

        // 落下中なら
        if(Play.My != 0) Play.Mx = 0;

        Play.Px += Play.Mx;
        Play.Py += Play.My;
    }

}

Re:落下判定について

Posted: 2010年6月03日(木) 08:04
by ookami
分かりませんが(というかあちこちコメントアウトされていてどれを活かしたいか分からなかったので)、PlayMove関数の動きが意図通りにいかないなら部分処理を関数化して単体テストする方向でいかがでしょうか。

例えば、
・前方に壁があるか判定してtrueかfalseを返す関数
・下に床があるか判定してtrueかfalseを返す関数
あたりから。

Re:落下判定について

Posted: 2010年6月03日(木) 11:48
by 伊藤
ookami様

回答ありがとうございます。
ご指摘ありがとうございます。
コメントを消して修正した最新版を書かせて頂きます。

関数に分けるのではなくY座標の判定をとってからX座標の判定をするようにしてみました。
// ここの条件が思いつかないです。
と書いてあるところの条件がどうしても思いつきません・・・。
void PlayMove(void){

    // 移動量のセット
    for(int speed=0; speed<Play.Speed; speed++){
        
        // 移動量の初期化
        bool turnF = false;
        
        Play.Mx = 0;
        Play.My = 1;
        Play.Ox = Play.Px;
        Play.Oy = Play.Py;
        Play.Cx = int(Play.Px-16 / CEL_SW);
        Play.Cy = int(Play.Py-16 / CEL_SH);        
        
        // Y座標の判定
        for(int i=0; i<OBJ_MAX; i++){
            if(Map.Ptn == 0){
                // ここの条件が思いつかないです。
                if((Play.Px-16)%CEL_SW == 0 && Play.Px = Map.Px && Play.Py){
                    Play.Py = 1;
                }
            }
        }

        Play.Py += Play.My;

        if(Play.My == 0){
            // X座標の判定
            Play.Mx = 1;
            if(Play.Dir) Play.Mx *= -1;
            Play.Px += Play.Mx;
        
            // 左右の判定
            for(int i=0; i<OBJ_MAX; i++){
                // 0でも100でも101でもなかったら
                if(Map.Ptn != 0 && Map.Ptn != 100 && Map.Ptn != 101){
                    if(Play.Dir == RIGHT && (Play.Px-16)%CEL_SW == 0
                        && Play.Py == Map.Py && Play.Px+CEL_SW == Map.Px) turnF = true;
                    else if(Play.Dir == LEFT && (Play.Px-16)%CEL_SW == 0    
                        && Play.Py == Map.Py && Play.Px-CEL_SW == Map.Px) turnF = true;
                }
            }

            // 左右の反転
            if(turnF){
                Play.Px = Play.Ox;
                Play.Dir = !Play.Dir;
            }        
        }
    }

}

Re:落下判定について

Posted: 2010年6月03日(木) 11:49
by 伊藤
すみません・・・。
初期化のPlay.My = 1;は
Play.My = 0;でした。

Re:落下判定について

Posted: 2010年6月03日(木) 12:29
by ookami
むむむ... まぁ私が言ったのはデバッグ手法のほんの一例ではありますけどね。
数えると、
代入文 16箇所、
if文 8箇所、
  うち && でさらに分岐するのが 10箇所、
2重 forループもある、
てな規模で、しかもそれが思い通りに動かないとなれば、処理を部分ごとに分割して単体テストするべきかと私は思います。

とりあえず、アップしてもらったソースについてですが...
Playの、Mx,My,Ox,Oy,Px,Py,Cx,Cy の意味を解説してもらえますか?
あと、
>Play.My = 0;でした。
とすると、
Play.Myが常にゼロですが、意図通りですか?

Re:落下判定について

Posted: 2010年6月03日(木) 12:45
by 伊藤
回答ありがとうございます。

Mx,My…移動量
Ox,Oy…前座標
Px,Py…描画位置
Cx,Cy…セル座標(中心点描画をしている為 座標は32*x+16です)

処理が多いのはわかっております・・・。
もちろん現在のソースでは32で割り切れないとバグが出ると言った
色々な問題点があることも理解しております。
ただどうしても減らしたり効率がよいロジックが思いつきません・・・。

ゼロを入れているのは毎フレーム移動はしておりません。
下が0ならYの移動量が1入りそれをspeed回数分まわしおります。

ookami様がおっしゃった部分ごとに作れるなら作りたいのですが
下にマップがあるかなどの判定の仕方がわかりません・・・。

本当に申し訳ないのですがサンプルを頂けないでしょうか?
丸投げ+手間をかけてしまって申し訳ないのですが
どうやっても自分では解決できないです・・・。

Re:落下判定について

Posted: 2010年6月03日(木) 13:11
by 伊藤
何度もすみません。
bool HitLeftRight(void){
    
    bool hitF = false;
    
    for(int i=0; i<OBJ_MAX; i++){
        if(Map.Ptn != 0 && Map.Ptn != 100 && Map.Ptn != 101){
            if(Play.Dir == RIGHT && (Play.Px-16)%CEL_SW == 0
                && Play.Py == Map.Py && Play.Px+CEL_SW == Map.Px){
                    hitF = true;
                    break;
            }else if(Play.Dir == LEFT && (Play.Px-16)%CEL_SW == 0    
                && Play.Py == Map.Py && Play.Px-CEL_SW == Map.Px){
                    hitF = true;
                    break;
            }
        }
    }
        
    return hitF;
}

bool HitDown(void){

    bool hitF = false;
    
    for(int i=0; i<OBJ_MAX; i++){
        if(Map.Ptn == 0){ 
            if((Play.Px-16)%CEL_SW == 0 && Play.Px == Map.Px && Play.Py > Map.Py+CEL_SH && (Play.Py-Map[i].Py) < 32){
                hitF = true;
                break;
            }
        }
    }
        
    return hitF;
    
}

void PlayMove(void){
    
    // 移動量のセット
    for(int speed=0; speed<Play.Speed; speed++){
        
        // 移動量の初期化
        bool downF = false;
        bool turnF = false;
        
        Play.Mx = 0;
        Play.My = 0;
        Play.Ox = Play.Px;
        Play.Oy = Play.Py;
        
        // 落下判定
        downF = HitDown();
        if(!downF) Play.My = 1;
        
        // 左右の判定
        turnF = HitLeftRight();
        
        if(turnF){
            Play.Px = Play.Ox;
            Play.Dir = !Play.Dir;
        }else{
            if(Play.My == 0){
                Play.Mx = 1;
                if(Play.Dir) Play.Mx *= -1;
            }
        }

        Play.Px += Play.Mx;
        Play.Py += Play.My;

    }


判定をわけたものです。
やはり落下の判定が上手く取れません・・・
if文が問題なのかと思うのですがここの条件式が思い浮かびません・・。

Re:落下判定について

Posted: 2010年6月03日(木) 20:44
by ookami
おぉ、関数に分けてくださいましたね!ぜんぜん丸投げじゃないです。とても主体的だと思います。

単体テストのために、テストコードを書いてみましょう。

void testHitDown(void){
bool b;
FILE *fp;
fp=fopen("debug.txt","w");

// テストパターン1
Play.Px=32とか;
Play.Py=32とか;
:
: その他必要なパラメータ
:
b=HitDown();
fprintf(fp,"%d ",b);

// テストパターン2
Play.Px=なにがし;
Play.Py=なにがし;
:
: その他必要なパラメータ
:
b=HitDown();
fprintf(fp,"%d ",b);

以下、テストしたいパターンだけ繰り返し。

fclose(fp);
}

int メイン関数{
初期化処理;
testHitDown();
}

今あるメイン関数はいったんコメントアウトします。

これで、実行するとdebug.txtが生成されるはずですから、用意したテストパターンと予想した結果と照らし合わせてください。

ステップ実行などするのもよいでしょう。

原因がつかめなければ、
(Play.Px-16)%CEL_SW == 0 && Play.Px == Map.Px && Play.Py > Map.Py+CEL_SH && (Play.Py-Map.Py) < 32)
この部分が非常に複雑なので、ここだけさらに
bool HitDownElm(int i);
などとくくり出してもよいかもしれません。

サンプルどころかヒントすら示していなくてごめんなさい。しかし上記のように、関数を分けるだけでなく、固定値をつっこんでテストするということが非常に有効なので、ぜひやってみてください。

Re:落下判定について

Posted: 2010年6月03日(木) 23:27
by 伊藤
色々とアドバイスありがとうございます。
頂いたソースなのですが使い方がよくわかりません・・・。
変数出力を駆使してみたらどうもHitDownの判定が上手くいっておりません・・・。

小分けにしたいのですが今の自分では出来ないのでまとめて判定だけを処理するようにしたいです。
//////////////////////////////////////////////////
マップデータイメージ こんな感じのtblでまとめて読み込んでいます。
//////////////////////////////////////////////////
int MapData_01[CEL_H][CEL_W] = {
    { 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,1,1,1,2, 3,3,4,4,5, 5,1,1,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },

    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },

    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1 },
    { 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1 },
};
//////////////////////////////////////////////////

void InitMap(void){

    MapNum = 0;
    
    for(int i=0; i<CEL_H; i++){
        for(int j=0; j<CEL_W; j++){
            MapData[j] = *(MapTbl[MapNum] + i*CEL_W + j);
            //MapData[j] = MapData_05[j];
        }
    }
    
    // オブジェ初期化
    for(int i=0; i<OBJ_MAX; i++){
        Map.Cy = i / CEL_W;
        Map.Cx = i-(Map.Cy*CEL_W);
        Map.Ptn = MapData[Map.Cy][Map.Cx];
        Map.Px = float(Map[i].Cx*CEL_SW + CEL_SW/2);
        Map[i].Py = float(Map[i].Cy*CEL_SH + CEL_SH/2);
        Map[i].Air = 0xff;
        Map[i].Cnt = 0;

        if(Map[i].Ptn == 100){
            M_Start.x = Map[i].Cx;
            M_Start.y = Map[i].Cy;
        }
        if(Map[i].Ptn == 101){
            M_Goal.x = Map[i].Cx;
            M_Goal.y = Map[i].Cy;
        }
    }

}
//////////////////////////////////////////////////
bool HitDown(void){

    bool hitF = false;
    
    for(int i=0; i<OBJ_MAX; i++){
        if((Play.Px-16)%CEL_SW == 0 && Play.Px == Map[i].Px && Play.Cy-1 == Map[i].Cy){
            hitF = true;
            break;
        }
    }
    if((Play.Px-16)%CEL_SW == 0){
        /*
        if(){
            if((Play.Px-16)%CEL_SW == 0 && Play.Px == Map[i].Px && Play.Cy-1 == Map[i].Cy){
                hitF = true;
                break;
            }
        }
        */
    }
        
    return hitF;
    
}

Re:落下判定について

Posted: 2010年6月04日(金) 09:47
by 伊藤
かなり無理やりですがなんとか出来ました・・・
bool HitLeftRight(void){
    
    bool hitF = false;
    
    for(int i=0; i<OBJ_MAX; i++){
        /*
        if(Map.Ptn != 0 && Map.Ptn != 100 && Map.Ptn != 101){
            if(Play.Dir == RIGHT){
                if(Play.Px-CEL_SW/2 < Map.Px-CEL_SW/2+1 && Play.Px+CEL_SW/2 > Map.Px-CEL_SW/2 &&
                    Play.Py-CEL_SH/2 < Map.Py+CEL_SH/2 && Play.Py+CEL_SH/2 > Map.Py-CEL_SH/2){
                        hitF = true;
                        break;
                }
            }else if(Play.Dir == LEFT){
                if(Play.Px-CEL_SW/2 < Map.Px+CEL_SW/2 && Play.Px+CEL_SW/2 > Map.Px+CEL_SW/2-1 &&
                    Play.Py-CEL_SH/2 < Map.Py+CEL_SH/2 && Play.Py+CEL_SH/2 > Map[i].Py-CEL_SH/2){
                        hitF = true;
                        break;
                }
            }
        }        
        */
        if(Map[i].Ptn != 0 && Map[i].Ptn != 100 && Map[i].Ptn != 101){
            if(Play.Dir == RIGHT 
                && Play.Cy == Map[i].Cy && Play.Px+CEL_SW == Map[i].Px){
                    hitF = true;
                    break;
            }else if(Play.Dir == LEFT     
                && Play.Cy == Map[i].Cy && Play.Px-CEL_SW == Map[i].Px){
                    hitF = true;
                    break;
            }
        }
        
        
    }
        
    return hitF;
}

bool HitDown(void){

    bool hitF = false;
    for(int i=0; i<OBJ_MAX; i++){
        if(Map[i].Ptn != 0){
            if(Play.Px-CEL_SW/2 < Map[i].Px+CEL_SW/2 && Play.Px+CEL_SW/2 > Map[i].Px-CEL_SW/2 &&
                Play.Py-CEL_SH/2 < Map[i].Py-CEL_SH/2+1 && Play.Py+CEL_SH/2 > Map[i].Py-CEL_SH/2){
                    hitF = true;
                    break;
            }
        }
    }
    
    return hitF;
    
}

void PlayMove(void){
    
    // 移動量のセット
    for(int speed=0; speed<Play.Speed; speed++){
        
        // 移動量の初期化
        bool HitDownF = false;
        bool HitLeftRightF = false;
        
        Play.Mx = 0;
        Play.My = 0;
        Play.Ox = Play.Px;
        Play.Oy = Play.Py;
        Play.Cx = int(Play.Px-16) / CEL_SW;
        Play.Cy = int(Play.Py-16) / CEL_SH;
        
        // 落下判定
        HitDownF = HitDown();
        HitLeftRightF = HitLeftRight();
        tf= HitDownF;
        //tf= HitLeftRightF;
        
        //tf = HitDownF;
        
        // 床と当たってなかったら落下 当たってたら左右移動
        if(HitDownF){
            if(HitLeftRightF){
                Play.Px = Play.Ox;
                Play.Dir = !Play.Dir;
            }else{
                Play.Mx = 1;
                if(Play.Dir) Play.Mx *= -1;
            }            
        }else{
            Play.My = 1;
        }

        Play.Px += Play.Mx;
        Play.Py += Play.My;
    }
}


これの改善案的なもののアドバイスを頂けないでしょうか?

Re:落下判定について

Posted: 2010年6月04日(金) 12:53
by wing
伊藤さんこんにちは。
自力で当たり判定を作成するなんて感心してみてました。
私なんかは他の人のサンプルを流用させてもらってばかりです。

参考にはならないと思いますが、superMariko2 のコードを
アップしますので、ほとんどが当たり判定なので、よかったら見てください。

Re:落下判定について

Posted: 2010年6月04日(金) 14:23
by ookami
出来たとのこと、よかったです^^ あまりお役に立てませんで... 私も書き方を考えないといかんですね。

(ここまでは、意図通りに動かないプログラムへの対処でしたが、
 ここからは、意図通りには動くんだけど、ソースをどう改善するか、という話ですね)

総論的に、リファクタリングについてはこちら。
http://ja.wikipedia.org/wiki/%E3%83%AA% ... %E3%82%B0_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)

提案としては以下です。

if( Play.Px-CEL_SW/2 < Map.Px+CEL_SW/2 && Play.Px+CEL_SW/2 > Map.Px-CEL_SW/2 &&
Play.Py-CEL_SH/2 < Map.Py-CEL_SH/2+1 && Play.Py+CEL_SH/2 > Map.Py-CEL_SH/2 ){

↑こうなっているところを、if文の中身をくくりだして↓

bool hitRect(float ax1,float ay1,
float ax2,float ay2,
float bx1,float by1,
float bx2,float by2) {
return ( ax1 < bx2 && ax2 > bx1 && ay1 < by2 && ay2 > by1 );
}

:
:

if( hitRect(Play.Px-CEL_SW/2,Play.Py-CEL_SH/2,
Play.Px+CEL_SW/2,Play.Py+CEL_SH/2,
Map.Px-CEL_SW/2,Map.Py-CEL_SH/2+1,
Map.Px+CEL_SW/2,Map.Py-CEL_SH/2 )) {

こんな感じにします。引数については添付ファイルをご覧ください。
こうしておけば、今後、長方形同士の当たり判定に毎回使えます。
参考になれば幸いです。