ボールの接触判定とバーの移動について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ラーグ

ボールの接触判定とバーの移動について

#1

投稿記事 by ラーグ » 15年前

[1] 質問文
 [1.1] ブロック崩しのようなイメージで動いているバーにボールが当たったら跳ね返るようにしたいです
バーもボールと同じ座標で動かないよう各々動かしたいです
 [1.2]
#include <stdio.h>
#include <stdlib.h>
#include <eggx.h>

int win;

int square(void){
  newpen(win, 2);
  fillrect(win, 150.0, 150.0, 200.0, 200.0);
}
int upbar(float x){
  float dx;
  dx=5.0;

  newpen(win, 9);
  fillrect(win, 320.0+x, 480.0, 80.0, 20.0);
  x+= dx;
  if(x>480.0){
    x-= dx;
  }
  else if(x<20.0){
    x+= dx;
  }
}
int downbar(float x){
  float dx;
  dx=5.0;
  
  newpen(win, 9);
  fillrect(win, 50.0+x, 0.0, 80.0, 20.0);
  x+= dx;
  if(x>480.0){
    x-= dx;
  }
  else if(x<20.0){
    x+= dx;
  }
}
int rightbar(float y){
  float dy;
  dy=5.0;

  newpen(win, 9);  
  fillrect(win, 480.0, 260.0+y, 20.0, 80.0); 
  y+= dy;
  if(y>480.0){
    y-= dy;
  }
  else if(y<20.0){
    y+= dy;
  }
}
int leftbar(float y){
  float dy;
  dy=5.0;

  newpen(win, 9);
  fillrect(win, 0.0, 180.0+y, 20.0, 80.0);
  y-= dy;
  if(y>480.0){
    y-= dy;
  }
  else if(y<20.0){
    y+= dy;
  }
}

int main(){  
  float x, y, dx, dy;

  win=gopen(500,500);//描画ウインドウを開く
  winname(win, "reflect");//名前をつける
  gsetbgcolor(win, "white");//背景を白に指定
  gclr(win);//背景を白に塗りつぶす

  x=40.0;
  y=30.0;
  dx=10.0;
  dy=10.0;
  
  while(1){
    gclr(win);
    square(); //中央に赤の正方形を表示
    upbar(x); //上側に灰の長方形を表示
    downbar(x); // 下側に灰の長方形を表示
    rightbar(y); // 右側に灰の長方形を表示
    leftbar(y); // 左側に灰の長方形を表示

    newpen(win, 5);
    fillarc(win, x, y, 10.0, 10.0, 0.0, 360.0, 1);//丸を表示
    x+= dx;  
    y+= dy;
    if( 150.0<=x && x<=350.0 && y==140.0 ){
      dy= -dy; //中央ブロック下端に当たった時の処理
    }   
    else if( 150.0<=x && x<=350.0 && y==360.0 ){
      dy= -dy; //中央ブロック上端に当たった時の処理
    }   
    else if( 150.0<=y && y<=350.0 && x==360.0 ){
      dx= -dx; //中央ブロック右端に当たった時の処理
    }   
    else if( 150.0<=y && y<=350.0 && x==140.0 ){
      dx= -dx; //中央ブロック左端に当たった時の処理
    }   
    else if( x==140 && y==140 || x==360 && y==140 || x==360 && y==140 || x==360 && y==360 ){
      dx= -dx;
      dy= -dy; //中央ブロックの角に当たった時の処理
    }
    msleep(70);
  }
}


サブルーチンを使わないといけないのでmain関数の中のxとyを引数に持ってきて上記のプログラムの様にしました

 [1.3] バーの動かし方が分からずまた接触判定をどうすればいいのか困っていますので、サブルーチンの中でどう書けばいいのか教えていただきたいです

[2] 環境  
 [2.1] OS : Mac OS X
 [2.2] コンパイラ名 : egg

[3] その他
 初心者なのでほとんど分からない状態です
 期限が間近なので早めに回答頂けたら幸いです
 

softya

Re:ボールの接触判定とバーの移動について

#2

投稿記事 by softya » 15年前

宿題か課題ですよね?
いつまでの締切かハッキリさせて貰うとみなさんの気構えが変わりますので明確にして下さい。

この掲示板でMACが使える人は限られていると思いますので、画面イメージがあったら貼りつけてももらえますか。どうもイメージが分からなくて。ちなみに私もMACはありません。

あと幾つか疑問が。
・各バーの移動方向は?
・バーの移動条件と言うか、キーボードの入力で動かすんですか?
・キーボードから入力方法は?
・終了はないのでしょうか?
・ボールが画面外へ出た時の処理は?
・自分はどこまでC言語を理解しているのか、学校ではどこまで習ったのか明確にして下さい。特に使って良い関数の制限などが分かると答えやすいです。無制限なら無制限と書いて下さいね

ラーグ

Re:ボールの接触判定とバーの移動について

#3

投稿記事 by ラーグ » 15年前

大学の課題で提出日は1月25日です

・上下のバーは左右に、左右のバーは上下に各々移動させようと思っています
・キーボードでの入力で動かすのではなく、プログラムを開始して後は自動で動くようにするつもりです
・終了は無いです
・ボールが画面外へ出たときの処理はまだ未定です・・・
・学校で習った範囲は
ループ
二重ループ
配列
条件分岐
入力と出力
などの基本的な内容は勉強しました

言語によっての違いなどがよく分からないのでとりあえず習った内容についてはこういう返答しかできないので申し訳無いです・・・

softya

Re:ボールの接触判定とバーの移動について

#4

投稿記事 by softya » 15年前

時間が無いとのことですので、がんがん仕様を固めましょう。

・上下のバーは左右に、左右のバーは上下に各々移動させようと思っています
・キーボードでの入力で動かすのではなく、プログラムを開始して後は自動で動くようにするつもりです
自由(ランダム)移動ですか?
それともAI?
なにか明確なルールを決めて下さい。

・ボールが画面外へ出たときの処理はまだ未定です・・・
少なくとも再スタートか反射か決めて下さい。
後で決めようなんてのは時間が無いので、すぐ決めましょう。

それと、構造体の配列や構造体の引数は使えますか?
構造体のポインタも使いたいところです。
なので、構造体のメンバを考えてみて下さい。

バーも正方形も全部構造体化して表示や当たり判定サブルーチンは出来るだけ1つにまとめてたいです。
バーの移動サブルーチン1つで済ませたいですね。

ラーグ

Re:ボールの接触判定とバーの移動について

#5

投稿記事 by ラーグ » 15年前

・バーは玉の座標と関係なくそれぞれが独立させて動かそうと思っています(これがAIなのかどうか判別できないのではっきりとした返答になっているか不安ですが・・・)
移動距離や進行方向は上記プログラムの様にして一定座標まで進行方向を逆にする様にしています

・ボールが画面外へ出た時は再スタートの形をとろうと思います

・構造体についてはまだ習っていないです
使ってはいけないということは無いですが期限以内に自分が構造体について把握、対処できるのかどうか分かりません

softya

Re:ボールの接触判定とバーの移動について

#6

投稿記事 by softya » 15年前

>・バーは玉の座標と関係なくそれぞれが独立させて動かそうと思っています(これがAIなのかどうか判別できないのではっきりとした返答になっているか不安ですが・・・)
>移動距離や進行方向は上記プログラムの様にして一定座標まで進行方向を逆にする様にしています
そうすると、一定方向に一定速度で動いて、一定座標まで来たら方向を逆転、逆に移動してまた逆転。これを繰り返すって事で良いですね。

>・ボールが画面外へ出た時は再スタートの形をとろうと思います
分かりました。

>・構造体についてはまだ習っていないです
じゃぁ構造体は諦めて、配列だけ使いましょう。

まず、グローバル変数を決めましょう。
ボールの座標、半径、ベクトルをグローバル変数とします。これは配列は使いません。
同様に四角形(中央とバーを兼ねます)のグローバル配列(各要素数は5)を作りましょう。
四角形の座標、サイズ、ベクトル(中央の四角は0です)、移動回数カウンタ(これが0になったら逆方向に逆転)を定義します。
この変数や配列の宣言とボールと四角形の変数や配列を初期化する関数を書いてみて下さい。
特に四角形は、出来るだけforループなどを使って同じことは書かない様に工夫してみて下さい。
では、また明日!

ラーグ

Re:ボールの接触判定とバーの移動について

#7

投稿記事 by ラーグ » 15年前

ボールの座標などのグローバル変数と、バーの座標などのグローバル変数はそれぞれ別々なんですか?

できればこんな感じのソースというのを書いて頂けないでしょうか?

softya

Re:ボールの接触判定とバーの移動について

#8

投稿記事 by softya » 15年前

ここの掲示板の返事を待つより、とりあえずやってみた方が早いですよ。

>ボールの座標などのグローバル変数と、バーの座標などのグローバル変数はそれぞれ別々なんですか?

いっしょにするメリットはないですからね。

>できればこんな感じのソースというのを書いて頂けないでしょうか?

ボールはグローバル化するだけですから、四角形の例を。

int sq_x[5],sq_y[5]; //座標
int sq_sizex[5],sq_sizey[5]; //サイズ
以下略。

ラーグ

Re:ボールの接触判定とバーの移動について

#9

投稿記事 by ラーグ » 15年前

グローバル配列のやり方が分からず完全に行き詰まっています...
ベクトルはまだいじっていません
このやり方であっているんでしょうか?
#include <stdio.h>
#include <stdlib.h>
#include <eggx.h>

int win;
float x, y, dx, dy; 
float sq_x[5], sq_y[5];
float sq_w[5], sq_h[5];
float sq_dx[5], sq_dy[5];

sq_x[0]=150.0; sq_y[0]=150.0; sq_w[0]=200.0; sq_h[0]=200.0; sq_dx[0]=200.0; sq_dy[0]=200.0;   
sq_x[1]=320.0  sq_y[1]=480.0; sq_w[1]=80.0;  sq_h[1]=20.0;  sq_dx[0]=200.0; sq_dy[0]=200.0; 
sq_x[2]=50.0;  sq_y[2]=0.0;   sq_w[2]=80.0;  sq_h[2]=20.0;  sq_dx[0]=200.0; sq_dy[0]=200.0; 
sq_x[3]=480.0; sq_y[3]=260.0; sq_w[3]=20.0;  sq_h[3]=80.0;  sq_dx[0]=200.0; sq_dy[0]=200.0; 
sq_x[4]=0.0;   sq_y[4]=180.0; sq_w[4]=20.0;  sq_h[4]=80.0;  sq_dx[0]=200.0; sq_dy[0]=200.0;
 


int square(void){
  newpen(win, 2);
  fillrect(win, , x[0], y[0], w[0], h[0]);
}
int upsquare(void){
  float x, dx;
  x=0.0;
  dx=5.0;

  newpen(win, 9);
  fillrect(win, , x[1], y[1], w[1], h[1]);
  x+= dx;
  if(x>480.0){
    x-= dx;
  }
  else if(x<20.0){
    x+= dx;
  }
}
int downsquare(void){
  float x, dx;
  x=0.0;
  dx=5.0;
  
  newpen(win, 9);
  fillrect(win, , x[2], y[2], w[2], h[2]);
  x+= dx;
  if(x>480.0){
    x-= dx;
  }
  else if(x<20.0){
    x+= dx;
  }
}
int rightsquare(void){
  float y, dy;
  y=0.0;
  dy=5.0;

  newpen(win, 9);  
  fillrect(win, , x[3], y[3], w[3], h[3]); 
  y+= dy;
  if(y>480.0){
    y-= dy;
  }
  else if(y<20.0){
    y+= dy;
  }
}
int leftsquare(void){
  float y, dy;
  y=0.0;
  dy=5.0;

  newpen(win, 9);
  fillrect(win, , x[4], y[4], w[4], h[4]);
  y-= dy;
  if(y>480.0){
    y-= dy;
  }
  else if(y<20.0){
    y+= dy;
  }
補足の質問でグローバル配列ができたとしてforループをどのように利用すればいいんでしょうか?
また移動カウンタとはどういうことですか?
質問ばかりですいません...

ラーグ

Re:ボールの接触判定とバーの移動について

#10

投稿記事 by ラーグ » 15年前

プログラムを少し修正
int win;
float x, y, dx, dy; 
float sq_x[5], sq_y[5];
float sq_w[5], sq_h[5];
float sq_dx[5], sq_dy[5];

int square(void){
  sq_x[0]=150.0; sq_y[0]=150.0; sq_w[0]=200.0; sq_h[0]=200.0; sq_dx[0]=0.0; sq_dy[0]=0.0;  
  newpen(win, 2);
  fillrect(win, sq_x[0], sq_y[0], sq_w[0], sq_h[0]);
}
int upsquare(void){
  sq_x[1]=320.0; sq_y[1]=480.0; sq_w[1]=80.0;  sq_h[1]=20.0;  sq_dx[1]=5.0; sq_dy[1]=0.0; 
  newpen(win, 9);
  fillrect(win, sq_x[1], sq_y[1], sq_w[1], sq_h[1]);
  sq_x[1]+= sq_dx[1];
  if(sq_x[1]>480.0){
    sq_x[1]-= sq_dx[1];
  }
  else if(sq_x[1]<20.0){
    sq_x[1]+= sq_dx[1];
  }
}
int downsquare(void){
  sq_x[2]=50.0;  sq_y[2]=0.0;   sq_w[2]=80.0;  sq_h[2]=20.0;  sq_dx[2]=5.0; sq_dy[2]=0.0; 
  newpen(win, 9);
  fillrect(win, sq_x[2], sq_y[2], sq_w[2], sq_h[2]);
  sq_x[2]+= sq_dx[2];
  if(sq_x[2]>480.0){
    sq_x[2]-= sq_dx[2];
  }
  else if(sq_x[2]<20.0){
    sq_x[2]+= sq_dx[2];
  }
}
int rightsquare(void){
  sq_x[3]=480.0; sq_y[3]=260.0; sq_w[3]=20.0;  sq_h[3]=80.0;  sq_dx[3]=0.0; sq_dy[3]=5.0; 
  newpen(win, 9);  
  fillrect(win, sq_x[3], sq_y[3], sq_w[3], sq_h[3]); 
  sq_y[3]+= sq_dy[3];
  if(sq_y[3]>480.0){
    sq_y[3]-= sq_dy[3];
  }
  else if(sq_y[3]<20.0){
    sq_y[3]+= sq_dy[3];
  }
}
int leftsquare(void){
  sq_x[4]=0.0;   sq_y[4]=180.0; sq_w[4]=20.0;  sq_h[4]=80.0;  sq_dx[4]=0.0; sq_dy[4]=5.0; 
  newpen(win, 9);
  fillrect(win, sq_x[4], sq_y[4], sq_w[4], sq_h[4]);
  sq_y[4]+= sq_dy[4];
  if(sq_y[4]>480.0){
    sq_y[4]-= sq_dy[4];
  }
  else if(sq_y[4]<20.0){
    sq_y[4]+= sq_dy[4];
  }
}
前の投稿では変数の名前を書き換えてなかったので直しました
エラーは出なくなりましたが、バーが止まったままで動かないです...

softya

Re:ボールの接触判定とバーの移動について

#11

投稿記事 by softya » 15年前

これは初期化関数で書いた方が良いですね。
配列の初期値で書いても良いんですが、混乱しそうなので。
sq_x[0]=150.0; sq_y[0]=150.0; sq_w[0]=200.0; sq_h[0]=200.0; sq_dx[0]=200.0; sq_dy[0]=200.0; 
sq_x[1]=320.0 sq_y[1]=480.0; sq_w[1]=80.0; sq_h[1]=20.0; sq_dx[0]=200.0; sq_dy[0]=200.0;
sq_x[2]=50.0; sq_y[2]=0.0; sq_w[2]=80.0; sq_h[2]=20.0; sq_dx[0]=200.0; sq_dy[0]=200.0;
sq_x[3]=480.0; sq_y[3]=260.0; sq_w[3]=20.0; sq_h[3]=80.0; sq_dx[0]=200.0; sq_dy[0]=200.0;
sq_x[4]=0.0; sq_y[4]=180.0; sq_w[4]=20.0; sq_h[4]=80.0; sq_dx[0]=200.0; sq_dy[0]=200.0;
なので、四角形の初期化関数を作って、そこに書いて下さい。
ちなみに、各バーの移動ベクトルは違うはずですよね?
中央の四角形は動かいないのでベクトルが0だと思いますが。

>補足の質問でグローバル配列ができたとしてforループをどのように利用すればいいんでしょうか?
同じ値を入れるときに利用して下さい。
出てこなければ使わなくても良いです。

>また移動カウンタとはどういうことですか?
>質問ばかりですいません...

あぁ、そこが説明不足でしたね。
各バーを移動させるとして、どうやって移動方向を逆転させるかですが。
私の思いついたのか一定回数移動したら移動ベクトルを逆転すれば良いんじゃないかってアイデアです。
でカウンタを設けてカウントダウンしていって0になったら移動ベクトルを逆転します。
0になったカウンタには、また次に逆転してほしいタイミングのカウント値を入れます。
これでカウントが0になる度に方向が逆転するって仕組みになるわけです。

ラーグ

Re:ボールの接触判定とバーの移動について

#12

投稿記事 by ラーグ » 15年前

アドバイス通りiをカウンタにして一定距離進んだら移動方向を逆にすることが出来ました!
int win, i[4];
float x, y, dx, dy; //丸の座標と移動距離
float sq_x[5], sq_y[5]; //各四角形の座標
float sq_w[5], sq_h[5]; //各四角形のサイズ
float sq_dx[2], sq_dy[2]; //各四角形の移動距離

int square(void){
  newpen(win, 2);
  fillrect(win, sq_x[0], sq_y[0], sq_w[0], sq_h[0]);
}
int upsquare(void){  
  newpen(win, 9);
  fillrect(win, sq_x[1], sq_y[1], sq_w[1], sq_h[1]);
  sq_x[1]-= sq_dx[0];
  i[0]--;
  if(i[0]<0){
    sq_dx[0]= -sq_dx[0];
    i[0]=68;
  }
}
int downsquare(void){
  newpen(win, 9);
  fillrect(win, sq_x[2], sq_y[2], sq_w[2], sq_h[2]);
  sq_x[2]+= sq_dx[1];
  i[1]--;
  if(i[1]<0){
    sq_dx[1]= -sq_dx[1];
    i[1]=68;
  }
}
int rightsquare(void){
  newpen(win, 9);  
  fillrect(win, sq_x[3], sq_y[3], sq_w[3], sq_h[3]); 
  sq_y[3]+= sq_dy[0];
  i[2]--;
  if(i[2]<0){
    sq_dy[0]= -sq_dy[0];
    i[2]=68;
  }
}
int leftsquare(void){
  newpen(win, 9);
  fillrect(win, sq_x[4], sq_y[4], sq_w[4], sq_h[4]);
  sq_y[4]-= sq_dy[1];
  i[3]--;
  if(i[3]<0){
    sq_dy[1]= -sq_dy[1];
    i[3]=68;
  }
}
次に動いているバーへの当たり判定を追加したいんですがmain関数内の玉の挙動の部分に書くのか、それとも
バーの関数内に書くんでしょうか?
当たり判定のプログラム自体は以前に投稿した中央ブロックの当たり判定と同じ様に書けばいいと思っているのですが

softya

Re:ボールの接触判定とバーの移動について

#13

投稿記事 by softya » 15年前

まずですね。
square()
upsquare()
downsquare()
rightsquare()
leftsquare()
を1つの関数に出来ますので、してみませんか。
このままの仕組みで当たり判定に行くとソースコードが複雑怪奇になってしまいます。

1つにする場合作る関数は
square_all(int no)
{
//全部の四角形共通の処理
}
となります。
呼び出しは、こんな感じです。
int i;
for( i=0 ; i<5 ; i++ ) {
square_all(i);
}

あとi[4]は止めてcnt[5]としませんか。
なぜ5個なのかは、5つの関数を1つにするためです。
中央の四角形は動かないですが、上下左右のバーと同等に扱って下さい。
そうすれば、1つの関数で中央と上下左右のバーが扱えます。

>次に動いているバーへの当たり判定を追加したいんですがmain関数内の玉の挙動の部分に書くのか、それとも
バーの関数内に書くんでしょうか?

当たり判定は1つの関数にすべきでしょう。
for( i=0 ; i<5 ; i++ ) {
ball_atari_hanntei(i);
}
って感じで呼び出します。

すいません、続きはまた明日お願いします。

ラーグ

Re:ボールの接触判定とバーの移動について

#14

投稿記事 by ラーグ » 15年前

返信ありがとうございます
アドバイス参考に奮闘してみます!

ラーグ

Re:ボールの接触判定とバーの移動について

#15

投稿記事 by ラーグ » 15年前

四角形を一つの関数にまとめれたんですが、当たり判定の関数は四角形の関数とは別に分けて書くんですか?

後、当たり判定のやり方が以前書いた中央ブロックの当たり判定の様に書かないといけないんでしょうか?
前の返信では当たり判定の関数分けをしたほうがいいとあったのですが、どのように書けばいいのか中々思いつけません…

softya

Re:ボールの接触判定とバーの移動について

#16

投稿記事 by softya » 15年前

遅くなりました。

>四角形を一つの関数にまとめれたんですが、当たり判定の関数は四角形の関数とは別に分けて書くんですか?

関数は機能別に分けるのが構造化プログラミングの基本ですので。
後のメンテンス性を考えての事です。

>後、当たり判定のやり方が以前書いた中央ブロックの当たり判定の様に書かないといけないんでしょうか?
>前の返信では当たり判定の関数分けをしたほうがいいとあったのですが、どのように書けばいいのか中々思いつけません…

中央ブロックの当たり判定ですが、
if( 150.0<=x && x<=350.0 && y==140.0 ){
は、大分問題がありますね。
y==140.0になるかは運次第と言うか、浮動小数点で==比較はうまく機能しません。
※ 原因は浮動小数点のまるめ誤差。
今のままだと問題が

ちゃんとした円と矩形の当たり判定は、下記を参考にして下さい。
「2008-06-02: 円と矩形の当たり判定」
http://www.uxi.jp/diary/2008.html

で、当たり判定を
for( i=0 ; i<5 ; i++ ) {
atari_hanntei(i);
}
としていましたが、

上下左右の当た判定をちゃんとするために、次のようにします。
※ 前と変えています。
[よびだし]ボールの処理はball_mave()関数へ。
ball_move();


[ボールの処理]
ball_move()
{
// まずボールをX方向にベクトル分移動させる。
x = x + dx;
// 各四角形と当たり判定
if( atari_hantei() ) {
// 衝突したらX方向のベクトルを逆転。
dx = -dx;
}
// 続いてY方向にベクトル分移動させる。あとはY方向について同様の処理。
}

[当たり判定関数]
// もし衝突したら、1をreturn値として返す。
int atari_hantei()
{
// ボールと四角形の当たり判定。
for( i=0 ; i<5 ; i++ ) {
//サイトを参考に当たり判定を作って下さい。
}
// 衝突しなかったら
return 0;
}

って感じです。分からなかったら聞いてください。

ラーグ

Re:ボールの接触判定とバーの移動について

#17

投稿記事 by ラーグ » 15年前

ball_move()の関数を呼び出す際に()内にfillarcの各変数(自分のプログラムではx、y等)を入れて、ボールの動きの処理の関数の()内に(float x, float y)と書いて呼び出せばいいんでしょうか?
この方法で試してみたところ丸が移動しなくなって初期座標のところで止まったままになります・・・

後当たり判定のプログラムが見当がつかないので良ければ教えていただけないでしょうか

softya

Re:ボールの接触判定とバーの移動について

#18

投稿記事 by softya » 15年前

>ball_move()の関数を呼び出す際に()内にfillarcの各変数(自分のプログラムではx、y等)を入れて、ボールの動きの処理の関数の()内に(float x, float y)と書いて呼び出せばいいんでしょうか?
>この方法で試してみたところ丸が移動しなくなって初期座標のところで止まったままになります・・・

ボールの変数は、グローバル変数ですから引数で渡す必要がありません。
逆に引数で渡すと、値渡しになるため関数内で更新した値はグローバル変数に反映されず消えてしまいます。
これを防ぐ方法もありますが、ポインタを理解している必要がありますので今回は避けます。

>後当たり判定のプログラムが見当がつかないので良ければ教えていただけないでしょうか

じゃあ、とりあえず矩形と矩形の当たり判定で実装してみてもらえますか?
ボールは円に内接する矩形で当たり判定すると考えてみて下さい。
多少ウソが出ますが、初心者ならこのぐらいで良いのかも知れません。
参考↓
http://www012.upp.so-net.ne.jp/gyabo/tips/rect.html

ラーグ

Re:ボールの接触判定とバーの移動について

#19

投稿記事 by ラーグ » 15年前

softyaさんにアドバイスを頂いた
[ボールの処理]
ball_move()
{
 // まずボールをX方向にベクトル分移動させる。
 x = x + dx;
 // 各四角形と当たり判定
 if( atari_hantei() ) {
  // 衝突したらX方向のベクトルを逆転。
  dx = -dx;
 }
 // 続いてY方向にベクトル分移動させる。あとはY方向について同様の処理。
}

[当たり判定関数]
// もし衝突したら、1をreturn値として返す。
int atari_hantei()
{
 // ボールと四角形の当たり判定。
 for( i=0 ; i<5 ; i++ ) {
  //サイトを参考に当たり判定を作って下さい。
 }
 // 衝突しなかったら
 return 0;
}
この部分なんですがreturnで1を返すというのが分からなかったので
int atari_hantei(int i){
  for(i=0; i<5; i++){
    if( y==sq_y + sq_h + R && sq_x<=x && x<=sq_x + sq_w + R ){
      dy= -dy; //四角形上端に当たった時の処理
    }
    else if( y==sq_y - R && sq_x<=x && x<=sq_x + sq_w ){
      dy= -dy; //四角形下端に当たった時の処理
    }
    else if( x==sq_x + sq_w[i] + R && sq_y[i]<=y && y<=sq_y[i] + sq_h[i] ){
      dx= -dx; //四角形右端に当たった時の処理
    }
    else if( x==sq_x[i] - R && sq_y[i]<=y && y<=sq_y[i] + sq_h[i] ){
      dx= -dx; //四角形左端に当たった時の処理
    }
    else if(x==sq_x[i] - R && y==sq_y[i] - R || x==sq_x[i] - R && y==sq_y[i] + sq_h[i] + R || x==sq_x[i] + sq_w[i] + R && y==sq_y[i] - R || x==sq_x[i] + sq_w[i] + R && y==sq_y[i] + sq_h[i] + R ){
      dx= -dx; //四角形の角に当たったときの処理
      dy= -dy;
    }
    else if(y <= 0.0 || 500.0 <= y ){
      dy= -dy; //上下の壁に当たったときの処理
      newpen(win, 2);
      drawstr(win, 225.0, 250.0, 24, 0.0, "OUT!");
      msleep(10);
    }
    else if(x <= 0.0 || 500.0 <= x ){
      dx= -dx; //左右の壁に当たったときの処理
      newpen(win, 2);
      drawstr(win, 225.0, 250.0, 24, 0.0, "OUT!");
      msleep(10);
    }
  }
}

int ball_move(){
  newpen(win, 5);
  fillarc(win, x, y, 10.0, 10.0, 0.0, 360.0, 1);//円を表示
  x+= dx;
  y+= dy;
}


自分なりに書いてみて
ball_move(); //円を表示させる関数を呼び出す    
    for(i=0; i<5; i++)
      atari_hantei(i); //当たり判定の条件式の関数を呼び出す


関数の呼び出しをこのようにしてみましたが実際プログラムを動かしてみたところ、最初はちゃんと動作してくれるのですが途中でバーの中に入ってしまいおかしい動作をします
多分角を==で判定してしまっているのでダメなんだと思うんですが不等号を使って判定しようとしても配列全部に判定処理されるので、上側のバーの下端に当たるという判定と同時に下側のバーの下端の判定の処理と被ってちゃんと動作してくれません

( y>sq_y[i] - R && sq_x[i]<=x && x<=sq_x[i] + sq_w[i] ) //下端の条件式

(このように書いたら下側のバーの下端の判定処理でy>0で進行方向が逆になってしまう)

書き方が分かりづらくてすいませんがどなたか配列の場合での==を使わず判定処理をする方法を教えてもらえないでしょうか?
無駄に長文になってしまいすいませんm(__)m

softya

Re:ボールの接触判定とバーの移動について

#20

投稿記事 by softya » 15年前

>この部分なんですがreturnで1を返すというのが分からなかったので

分からないなら聞いて下さいね。
衝突条件の時に、return 1;
って値を返すだけです。
関数の戻り値は勉強されていますよね?

>書き方が分かりづらくてすいませんがどなたか配列の場合での==を使わず判定処理をする方法を教えてもらえないでしょうか?

私の書いたのは、今のプログラムの角の問題も解決できるプログラムですから書き方を変えちゃうと色々問題が出るはずです。
上端とか何処に当たっているか判定しなくて良いんです。当たるか当たらないかの判定だけで当たり判定は作れます。何処に当たるか調べるから==なんてのが出て来てしまいます。
あと画面外に出た時の処理は、別の関数でやってください。関数の機能の定義があいまいになります。

とりあえず、当たり判定のリンク先は読まれたんですよね?
参考↓
http://www012.upp.so-net.ne.jp/gyabo/tips/rect.html
だったら、
(mx1 <= ex2 && ex1 <= mx2 && my1 <= ey2 && ey1 <= my2)
の式になるはずですが。
とりあえず、当たり判定だけ書いてみてもらえませんか?
反射方向とか、上端下端とかは置いておいて、純粋に当たった事を判定する関数をです。

[追記]
左右と上下の移動と当たり判定を別にやると良いかは、色々シミュレーションしてみて考えてみて下さい。私の書いたプログラムの事です。
移動と当たり判定を次のようなアルゴリズムで処理します。
(1)座標にX方向だけの移動ベクトルを計算する。
(2)バー(四角形)との当たり判定を行う。当たっていたらXの移動ベクトルを反転する。
(3)X方向に移動した座標に、こんどはY方向の移動ベクトルを計算する。
(4)バー(四角形)との当たり判定を行う。当たっていたらYの移動ベクトルを反転する。



画像

ラーグ

Re:ボールの接触判定とバーの移動について

#21

投稿記事 by ラーグ » 15年前

ボールの処理と当たり判定の式について
int ball_move(){
  newpen(win, 5);
  fillarc(win, x, y, 10.0, 10.0, 0.0, 360.0, 1);//円を表示
  x= x + dx;
  if( atari_hantei() ){
    dx= -dx;
  }
  y= y + dy;
  if( atari_hantei() ){
    dy= -dy;
  }
}

int atari_hantei(int i){
  for(i=0; i<5; i++){
    if( (x-10.0) <= (sq_x+sq_w) && sq_x <= (x+10.0) && (y+10.0) <= sq_y && (sq_y+sq_h) <= (y-10.0) ){
      return 1;
    } 
      return 0;
  }
}



リンク先を参考に当たり判定の式を書いてみましたが、四角形を突き抜けてしまいます...
当たり判定の式自体は何度も見直したので大丈夫だとは思うんですが
変数iは一貫して共通にする必要は無いと思うのでmain関数内で宣言で大丈夫ですか?


main関数内の呼び出しも一応書いておきます
while(1){ 
    int i;
    gclr(win);
    for(i=0; i<5; i++){
      square_all(i); //各四角形を呼び出す
    }
            
    ball_move(); //円を表示させる関数を呼び出す
    if(y <= 0 || y >= 500){
      dy= -dy;
    }
    else if(x <= 0 || x >= 500){
      dx= -dx;
    }
    msleep(70);
  }
}


後、各四角形の座標をを初期化関数で書き直すのを以前にアドバイス頂いたんですがまだやっていません
見やすいかどうかだけならとりあえず現状維持にしておきます

[追記]
ball_move関数内についてなんですが
if( atari_hantei() ){
    dx= -dx;
  }
  y= y + dy;
  if( atari_hantei() ){
    dy= -dy;
  }

当たり判定の式でreturnで1(真)を返してX座標のベクトルを反転させるという動きをするんだと思うんですが
Y座標のベクトルを反転させる時は別の当たり判定の関数にしなければいけないですか?

今のままの当たり判定の式だと上下左右のどのバーに当たってもX,Yの両方のベクトルが反転すると思うんですが...
なので左右のバーの場合の当たり判定、上下のバーの場合の当たり判定を書かないといけないんでしょうか?

質問多くなってしまい本当すいません... 画像

softya

Re:ボールの接触判定とバーの移動について

#22

投稿記事 by softya » 15年前

コードを見直していますので、ちょっと待って下さいね。
お昼までに返答いたします。

あっ全体を見直したいのでできたらソースコードを添付してもらえませんか?
xx.txtの名前にしないと張り付けられないと思いますのがよろしくお願いします。 画像

softya

Re:ボールの接触判定とバーの移動について

#23

投稿記事 by softya » 15年前

>リンク先を参考に当たり判定の式を書いてみましたが、四角形を突き抜けてしまいます... 当たり判定の式自体は何度も見直した>ので大丈夫だとは思うんですが変数iは一貫して共通にする必要は無いと思うのでmain関数内で宣言で大丈夫ですか? main関数内>の呼び出しも一応書いておきます

まず、間違いを指摘するとreturn 0;の場所がまずいです。
>int atari_hantei(int i){ ←/引数はありません。
> for(i=0; i<5; i++){
> if( (x-10.0) <= (sq_x+sq_w) && sq_x <= (x+10.0) && (y+10.0) <= sq_y && (sq_y+sq_h) <= (y-10.0) ){ ← (mx1 <= ex2 && ex1 <= mx2 && my1 <= ey2 && ey1 <= my2) の式と違うような?
> return 1;
> }
> return 0; ←1個目で当たっていなかったらreturn 0;してしまっています。なので2つ目以降の四角形と衝突判定していません。
> }
>}
>

// 正しい処理
int atari_hantei(){
for(i=0; i<5; i++){
// わかりやすく記述しましょう。
// バグを出さないためには、まずわかり易い書き方からです。
float mx1 = sq_x;
float mx2 = sq_x+sq_w;
float ex1 = x-10.0;
float ex2 = x+10.0;
float my1 = sq_y;
float my2 = sq_y[i]+sq_h[i];
float ey1 = y-10.0;
float ey2 = y+10.0;
if( (mx1 <= ex2 && ex1 <= mx2 && my1 <= ey2 && ey1 <= my2) ) {
// 5個のうち、どれかに衝突したら1を返す。
return 1;
}
}
// 5個全部調べて当たりが無かったら0を返す。場所を変えました。
return 0;
}

>今のままの当たり判定の式だと上下左右のどのバーに当たってもX,Yの両方のベクトルが反転すると思うんですが...
>なので左右のバーの場合の当たり判定、上下のバーの場合の当たり判定を書かないといけないんでしょうか?

確かにまずい点がありました。
こちらで確認テストが出来ないので申し訳ないですね。
考え中なので、もう少しお待ち下さいね。

それと全体を見直したいので、ソースコードを添付してもらえませんか?
xx.txtの名前にしないと張り付けられないと思いますのがよろしくお願いします。

softya

Re:ボールの接触判定とバーの移動について

#24

投稿記事 by softya » 15年前

考え直した案ですが、図を見てもらえますか?
手書きで汚くて申し訳ないですが、真ん中黒い四角形がバーだと思って下さい。
で、ABCD4つのゾーンが定義されています。
これは、ボールの反射方向を衝突した時点の属するゾーンで決めようって仕組みになります。
ABゾーンだとYベクトルを反転します。
CDゾーンだとXベクトルを反転します。
両方のゾーンに属していたときは、X,Y両方のベクトルが反転されるわけです。

なので、ボール移動の関数は全く変わってしまいます。
当たり判定も当たったバーの番号を返すようにします。
int ball_move(){
newpen(win, 5);
fillarc(win, x, y, 10.0, 10.0, 0.0, 360.0, 1);//円を表示
x= x + dx;
y= y + dy;
 // 当たっているか判定する。
 int atari = atari_hantei();
 // -1以外なら衝突
if( -1 != atari ){
if( ABゾーンに衝突? ) {
dy = -dy;
}
if( CDゾーンに衝突? ) {
dx = -dx;
}
}
}

// 返す値を変更しました。
int atari_hantei(){
for(i=0; i<5; i++){
// わかりやすく記述しましょう。
// バグを出さないためには、まずわかり易い書き方からです。
float mx1 = sq_x;
float mx2 = sq_x+sq_w;
float ex1 = x-10.0;
float ex2 = x+10.0;
float my1 = sq_y;
float my2 = sq_y+sq_h;
float ey1 = y-10.0;
float ey2 = y+10.0;
if( (mx1 <= ex2 && ex1 <= mx2 && my1 <= ey2 && ey1 <= my2) ) {
// 5個のうち、どれかに衝突したら衝突した四角形の番号を返す。
return i;
}
}
// 5個全部調べて当たりが無かったら-1を返す。
return -1;
}

で、ゾーンのサイズですが、ボールが半径10なので各頂点から±10で良いと思います。
ゾーン判定のプログラムを考えてみて下さい。

もう、25日まで時間が無いので悠長に構えてられませんよ。
23日頃に終わらせる心構えでやらないと間に合わないと思います。 画像

ラーグ

Re:ボールの接触判定とバーの移動について

#25

投稿記事 by ラーグ » 15年前

返信ありがとうございます

できる限り早く完成させたいと思います!

[追記]
ソース添付しました

本文は私情だったんで削除しました 画像

softya

Re:ボールの接触判定とバーの移動について

#26

投稿記事 by softya » 15年前

ソース見ました。
ゾーン判定さえちゃんとすれば、ちゃんと反射してくれそうです。
私の環境にx windowとeggxも入れたので、コンパイル&実行出来ます。
※ x windowの使い方が不明でウィンドウの移動も出来ませんが(汗)
画面がチラつくので何か使い方が悪いのかも知れませんが、eggxに詳しくないのでご容赦です。

それとコンパイル時にwarningが出ていると思いますが、
int square_all(int i)
int ball_move()
は戻り値が無いので
void square_all(int i)
void ball_move()
が正しいです。

関数のプロトタイプ宣言もヘッダ直後に加えて下さい。
void square_all(int i);
void ball_move();
int atari_hantei();

以上です。

ゾーン判定のプログラムの完成をお待ちしています。

ラーグ

Re:ボールの接触判定とバーの移動について

#27

投稿記事 by ラーグ » 15年前

int atari_hantei(){
  for(i=0; i<5; i++){
    float mx1 = sq_x;
    float mx2 = sq_x+sq_w;
    float ex1 = x-10.0;
    float ex2 = x+10.0;
    float my1 = sq_y;
    float my2 = sq_y+sq_h;
    float ey1 = y-10.0;
    float ey2 = y+10.0;
    if(mx1 <= ex2 && ex1 <= mx2 && my1 <= ey2 && ey1 <= my2 ){
      return i;
    } 
  }
  return -1;
}


後はコンパイルのところまで持ってきたんですが、ball_move関数の方でエラーが起きてしまいます

reflect.c: In function ‘ball_move’:
reflect.c:35: error: ‘mx1’ undeclared (first use in this function)
reflect.c:35: error: (Each undeclared identifier is reported only once
reflect.c:35: error: for each function it appears in.)
reflect.c:35: error: ‘mx2’ undeclared (first use in this function)
reflect.c:35: error: ‘my2’ undeclared (first use in this function)
reflect.c:35: error: ‘my1’ undeclared (first use in this function)

変数の宣言はどこですればいいんでしょうか?
それと関数の呼び出しの部分は以前添付しましたソースと同じでいいんでしょうか?

softya

Re:ボールの接触判定とバーの移動について

#28

投稿記事 by softya » 15年前

>変数の宣言はどこですればいいんでしょうか?

関数の先頭で良いと思います。

>それと関数の呼び出しの部分は以前添付しましたソースと同じでいいんでしょうか?

はい。同じで良いと思います。

いちいち聞くより、自分で試した方が早いと思いますよ。
ソースコードの改変が気になるならコピーを残しておけば良いです。
質問して貰うのは全然問題ないですが、私が返答するまでの時間のロスが勿体無いです。 画像

softya

Re:ボールの接触判定とバーの移動について

#29

投稿記事 by softya » 15年前

締切が近づいてますが大丈夫ですか?
分からなかったら聞いて下さいね。

ラーグ

Re:ボールの接触判定とバーの移動について

#30

投稿記事 by ラーグ » 15年前

一応判定はできたと思いますが自分の中で完全に動作を理解できていません
ゾーン判定

四角形に当たった時も半径分、中に進んで跳ね返るので直そうとしたら当たり判定がおかしくなってしまい見た目が悪くなっています

今携帯からの書き込みなのでご希望ありましたら午後6時頃にソース添付しようと思います

softya

Re:ボールの接触判定とバーの移動について

#31

投稿記事 by softya » 15年前

>今携帯からの書き込みなのでご希望ありましたら午後6時頃にソース添付しようと思います
こちらでも動作確認出来ますので、お待ちしています。

ラーグ

Re:ボールの接触判定とバーの移動について

#32

投稿記事 by ラーグ » 15年前

返信大変遅れてしまいもうしわけありません

動作を見返したところ、いまだ当たり判定に不具合があったようで反射が
上手くいきません

ソース添付しておきますのでどこがおかしいかご指摘お願いします

softya

Re:ボールの接触判定とバーの移動について

#33

投稿記事 by softya » 15年前

非常に申し訳ないですが、今時間が取れません。
なんとか自力でお願いします。
ほんとうに申し訳ない。

ラーグ

Re:ボールの接触判定とバーの移動について

#34

投稿記事 by ラーグ » 15年前

分かりました、頑張ってみます
もし時間できたら返信お願いします
間に合っても間に合わなかってもとりあえず形にしたいので 画像

sizuma

Re:ボールの接触判定とバーの移動について

#35

投稿記事 by sizuma » 15年前

このライブラリは座標系が違うんですね。
あー考え難い。
とりあえず、yの反射を
if(x >= (mx1-R) && x <= (mx2+R) && y >= (my1-R) && y <= (my1) || x >= (mx1-R) && x <= (mx2+R) && y <= (my2+R) && y >= my2)
したらどーなりますか?

こちらでは動作確認できないので、どんな感じなのか分からないですが・・・
内容も上のコード以外あんまり見てないので。

ラーグ

Re:ボールの接触判定とバーの移動について

#36

投稿記事 by ラーグ » 15年前

添付しましたソースだと四角形に当たると当たり判定内から抜け出せなくなってしまいました
sizumaさんの考えてもらったの試してみましたが同じ結果でした
if(x >= (mx1-R) && x <= (mx2+R) && y >= my1 && y <= my1 || x >= (mx1-R) && x <= (mx2+R) && y <= my2 && y >= my2)
このように書くと反射は上手くいくんですが途中でバーに食い込んでしまい同じく当たり判定内から抜け出せなくなり、無限ループしました...

sizuma

Re:ボールの接触判定とバーの移動について

#37

投稿記事 by sizuma » 15年前

添付したファイルのソースに戻して、

if(x >= (mx1-R) && x <= (mx2+R) && y >= (my1-R) && y <= my1){
y = my1-R;
dy= -dy;
number =number + 1;
}else if(x >= (mx1-R) && x <= (mx2+R) && y >= (my2-R) && y <= my2){
y = my2 +R;
dy= -dy;
number =number + 1;
}

と書き直したらどうなりますかね?

ラーグ

Re:ボールの接触判定とバーの移動について

#38

投稿記事 by ラーグ » 15年前

反射の見た目がすごくスムーズでさっきまでは半径分埋まってから反射していたのが無くなりました!
ですがある程度進んで下側のバーに当たろうとした時に急に方向転換して無限ループにはまりました

sizuma

Re:ボールの接触判定とバーの移動について

#39

投稿記事 by sizuma » 15年前

うーん、どんな画面になってるのかイメージできない・・・
方向転換するのはy軸方向ですか?

x軸方向ならばとりあえず、x方向の反射も上のように書き直してみてください。
上のソースは
my1よりめりこんだら、球を上に出す
my2よりめりこんだら、球を下にだす
って意味です。

明日の朝か、2時間目が退屈な授業なので、11時くらいにはまた覗きにきますので。
がんばってください

ラーグ

Re:ボールの接触判定とバーの移動について

#40

投稿記事 by ラーグ » 15年前

簡単な図ですが...
ある程度球が進んでから下側のバー付近に行くと、
図のようにX座標を移動してX方向のベクトルが変わって画面外に出て無限ループになります

sizuma

Re:ボールの接触判定とバーの移動について

#41

投稿記事 by sizuma » 15年前

if( atari != -1){
if(x >= (mx1-R) && x <= (mx2+R) && y >= (my1-R) && y <= my1 || x >= (mx1-R) && x <= (mx2+R) && y >= (my2+R) && y <= my2){
dy= -dy;
number =number + 1;
}
if(y >= (my1-R) && y <= (my2+R) && x >= (mx1-R) && x <= mx1 || y >= (my1-R) && y <= (my2+R) && x >= (mx2+R) && x <= mx2){
dx= -dx;
number =number + 1;
}
if(number>15){
number = 2;
}

やっぱりここは+だと思いますけど・・・
うわーー遅刻するっ
また昼前にきます!
画像

softya

Re:ボールの接触判定とバーの移動について

#42

投稿記事 by softya » 15年前

時間がとれたので見直してみました。
1つの問題点は、壁の反射処理です。
if(y <= 10 || y >= 490){
dy= -dy;
}
else if(x <= 10 || x >= 490){
dx= -dx;
}
壁の角に行くとはまります。
if(y <= 10 || y >= 490){
dy= -dy;
}
if(x <= 10 || x >= 490){
dx= -dx;
}
が正しいです。

それと、これだと壁ぎわで乱反射しますので、
if(y <= 0 || y >= 500){
dy= -dy;
}
if(x <= 0 || x >= 500){
dx= -dx;
}
となります。
これでボールが何処かえ消えるのは防げると思います。

あと、ゾーン判定時にボールのベクトルを見て反射するか決めないとまずいかもです。

ラーグ

Re:ボールの接触判定とバーの移動について

#43

投稿記事 by ラーグ » 15年前

もう締め切り間近のため直しようがないので、ごり押しで乱反射対策しました

今まで壁に当たって反射する時にバーの当たり判定と被って無限ループに入っていたので
バーの高さが20.0なので壁に当たった時にその座標から±30.0移動させてベクトルを変えるようにしました

完全に計算式のみで動作できず不自然に見える部分も多々ありますが、一応無限ループは無くなったのでとりあえずこれで出そうと思います
また落ち着いたらソース添付しようと思います

皆さん詳しい解説をありがとうございました
本当に助かりました

ソース添付して解決とさせていただきます 画像

ラーグ

Re:ボールの接触判定とバーの移動について

#44

投稿記事 by ラーグ » 15年前

大変遅れてしまいました
課題の結果はまだ分かりませんがとりあえずの提出はできました
回答してくださった方々ほんとうにありがとうございました

閉鎖

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