お久しぶりです。
ブロック崩しがある程度できました。なにかアドバイスがもらえたらと思い書き込みました。
初心者なため、かなり見づらいプログラムになっています。申し訳ございません。
以下のサイトにアップしています。パスワードは「YT2013」です。
http://www1.axfc.net/uploader/so/2805218
一応個人的に気になった点をいくつか書いておきます
1.点数の表示のところになぜかたまに別の画像が表示されました。その別の画像自体いらなくなったので消しました。
一応、別の画像が表示されることはなくなったのですが、少し違和感があります。
2.アイテムを追加し、ブロック崩しによくあるバーにくっつくボールを作りました。バーにあたった位置にボールを固定する必要があります。そのためボールの位置をバーの移動量に合わせて動かす必要があるので、バーの移動制御の関数にくっつく場合のボール移動制御を書いてしまいました。なんとかボールの移動制御のほうでくっつく場合の移動制御をできないでしょうか。いっそのことバーとボールの移動制御の関数をまとめることも考えています。
ブロック崩し
Re: ブロック崩し
ソースコードを読ませていただきました。
まず、1の問題についてですが、プレイしてみると点数が上がるたびに表示が一瞬点滅している事がうかがえます。
これはimg_point配列の領域外へのアクセスを行っているため、正しくないグラフィックハンドルが描画関数に渡されたからだと考えられます。
(違う画像が表示されたのは、配列の領域外にたまたま有効なグラフィックハンドルが存在したからだと推測します)
問題の原因は恐らく2つあり、まず1つ目はblockhi1関数で点数を加算する時に19以上の値が算出されてしまうと
以下の繰り上がり処理の部分でバグが発生してしまいます。
仮にpoint[k]に35の値が入っていた場合を考えると、
point[k]= point[k] -10 の部分では計算結果が25になってしまい、配列の添え字に使用した場合に0~9の範囲に収まらず、
領域外アクセスになってしまうおそれがあります。
point[k+1] +=1 の部分にも問題があり、点数の繰り上がりは(35の場合)3つになるはずですが、1つしか繰り上がりません。
上記を踏まえて正しく計算するには、
繰り上がりの数は、point[k]の値を10で割ることで求める事ができ、(上記の例の場合 35 / 10 = 3つ)
繰り上がった後の残り(下1ケタ)は、その余りを使う事で求める事が出来ます。(上記の例の場合 35 % 10 = 5)
もう1つの原因は、点数の繰り上がり計算が描画の後に行われている事です。
正しく繰り上がり計算が行われるようになっても、繰り上がり前のpoint[k]の値が使われるため、
配列の領域外アクセスが起きてしまいます(上記の例でいくと、point[k]には35が入ったまま)。
これを解決するのは簡単で、繰り上がり計算を描画の前に持ってくるだけです。
次の2の問題についてですが、くっついた時(バーに接触)のボールのX座標がバーのX座標とどれくらい離れているかを記憶しておき、ボールの移動関数でバーの位置からボールの位置を算出する、というのはどうでしょうか。
あくまで、一つの手段にすぎないので、参考程度にしてください。
まず、1の問題についてですが、プレイしてみると点数が上がるたびに表示が一瞬点滅している事がうかがえます。
これはimg_point配列の領域外へのアクセスを行っているため、正しくないグラフィックハンドルが描画関数に渡されたからだと考えられます。
(違う画像が表示されたのは、配列の領域外にたまたま有効なグラフィックハンドルが存在したからだと推測します)
問題の原因は恐らく2つあり、まず1つ目はblockhi1関数で点数を加算する時に19以上の値が算出されてしまうと
// garph.cpp
void blockhit1(){
// 省略
//ボールとブロックのあたり判定
for(i = 0; i <7 ; i++){
for(j = 0; j < 9; j++){
// 省略
//ブロックがまだ消えていないときのブロックとボールの当たり判定
if(block1[i][j].flag == 0){
// 省略
//ブロックの各辺とボールとの当たり判定
if(HitLineandCircle(pt[0],ball.hit) == TRUE ||
HitLineandCircle(pt[1],ball.hit) == TRUE ||
HitLineandCircle(pt[2],ball.hit) == TRUE ||
HitLineandCircle(pt[3],ball.hit) == TRUE){
if(block1[i][j].life == 0){
// 省略
point[1] += 5 + hitnum*5; // ここの計算結果が19を超えると領域外アクセスが発生してしまう
}
}
}
}
}
// garph.cpp
void graph_board(){
// 省略
//点数の画像の変更
for(int k = 0;k<10;k++){
if(point[k] >=10){
point[k+1] +=1;
point[k]= point[k] -10; // point[k]に19以上の値が入っていた場合にバグが発生(9より上の値が算出されてしまう)
}
}
}
point[k]= point[k] -10 の部分では計算結果が25になってしまい、配列の添え字に使用した場合に0~9の範囲に収まらず、
領域外アクセスになってしまうおそれがあります。
point[k+1] +=1 の部分にも問題があり、点数の繰り上がりは(35の場合)3つになるはずですが、1つしか繰り上がりません。
上記を踏まえて正しく計算するには、
繰り上がりの数は、point[k]の値を10で割ることで求める事ができ、(上記の例の場合 35 / 10 = 3つ)
繰り上がった後の残り(下1ケタ)は、その余りを使う事で求める事が出来ます。(上記の例の場合 35 % 10 = 5)
もう1つの原因は、点数の繰り上がり計算が描画の後に行われている事です。
正しく繰り上がり計算が行われるようになっても、繰り上がり前のpoint[k]の値が使われるため、
配列の領域外アクセスが起きてしまいます(上記の例でいくと、point[k]には35が入ったまま)。
これを解決するのは簡単で、繰り上がり計算を描画の前に持ってくるだけです。
次の2の問題についてですが、くっついた時(バーに接触)のボールのX座標がバーのX座標とどれくらい離れているかを記憶しておき、ボールの移動関数でバーの位置からボールの位置を算出する、というのはどうでしょうか。
あくまで、一つの手段にすぎないので、参考程度にしてください。
Re: ブロック崩し
アドバイスありがとうございました。点数の表示、バーにボールがくっつく処理はアドバイス通りに変更したら
直りました。
もうひとつ聞きたかったこととして
初心者なため、かなり見づらいプログラムになっています。今は、main.cppにメニュー、キャラクター選択、コンフィングなどを書いていますが、分けたほうがよろしいでしょうか?
また、今は描画をgraph.cppでのみ行っていますが、ステージを増やしていくとかなり長くなってしまい。
ステージ以外の関数が見えにくくなってしまうと思っています。そこで、描画のソースを分けることも考えています。
しかし、ソースファイルを増やしすぎるのもいけないんじゃないかと思っています。
なにかプログラムの書き方にアドバイスがありましたら教えてください。お願いします。
直りました。
もうひとつ聞きたかったこととして
初心者なため、かなり見づらいプログラムになっています。今は、main.cppにメニュー、キャラクター選択、コンフィングなどを書いていますが、分けたほうがよろしいでしょうか?
また、今は描画をgraph.cppでのみ行っていますが、ステージを増やしていくとかなり長くなってしまい。
ステージ以外の関数が見えにくくなってしまうと思っています。そこで、描画のソースを分けることも考えています。
しかし、ソースファイルを増やしすぎるのもいけないんじゃないかと思っています。
なにかプログラムの書き方にアドバイスがありましたら教えてください。お願いします。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 15年前
- 住所: 東海地方
- 連絡を取る:
Re: ブロック崩し
気になる所があるので、書かせてもらいますね。
・ファイルの分割で分割の単位で物=オブジェクト意識して分割したほうが良いと思います。
ゲームプログラミングの館の「ゲームプログラミング設計」を参考にされてはどうでしょうか?
http://dixq.net/g/
・あちこちにマジックナンバーが有るのが気になります。
constやenumも積極的に使いましょう。
ただしやり過ぎに注意。
「[迷信] ソースコード中の即値を全廃せよ | 株式会社きじねこ」
http://www.kijineko.co.jp/tech/supersti ... iates.html
・graph.cppもそうですが、同じようなコードが散見されます。
もっと関数で共通化出来るんではないでしょうか?
staticな関数を増やしましょう!
・龍神録のGROBALな構造を止めて出来るだけstaticな変数にしてみましょう。
引数と戻り値をうまく使って下さい。
・ファイルの分割で分割の単位で物=オブジェクト意識して分割したほうが良いと思います。
ゲームプログラミングの館の「ゲームプログラミング設計」を参考にされてはどうでしょうか?
http://dixq.net/g/
・あちこちにマジックナンバーが有るのが気になります。
constやenumも積極的に使いましょう。
ただしやり過ぎに注意。
「[迷信] ソースコード中の即値を全廃せよ | 株式会社きじねこ」
http://www.kijineko.co.jp/tech/supersti ... iates.html
・graph.cppもそうですが、同じようなコードが散見されます。
もっと関数で共通化出来るんではないでしょうか?
staticな関数を増やしましょう!
・龍神録のGROBALな構造を止めて出来るだけstaticな変数にしてみましょう。
引数と戻り値をうまく使って下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ブロック崩し
アドバイスありがとうございます。
今できるだけ、グローバル変数を使わないように直しています。
あたり判定は一番悩みどころです。
今、必殺技とブロックのあたり判定の設定を行っています。必殺技に必要な変数
static int spstate; //必殺技の状態
static int spcnt = 0; //必殺技のカウント
static int sp2pos[3] = {0,0,0}; //必殺技の位置
static Rect2D sp1hit; //必殺技の当たり判定1
static Rect2D sp2hit[3]; //必殺技のあたり判定2
をstaticに変えました。
しかし、以前はblock.cppにもspstate,sp1hit,sp2hitを使っていました。
なので、char.cppにあたり判定の関数を作り
char.cpp
block.cpp
というように変えました。しかし、うまく判定されませんでした。どのようにあたり判定の設定を行えばようのか教えてください。お願いします。
あと、
新・ゲームプログラミングの館のゲームプログラミングの設計では、それぞれ初期化のところで画像の読み込みを行うように
なっていたのですがload関数を作ってまとめて読み込むというやり方ではいけないのですか。
今できるだけ、グローバル変数を使わないように直しています。
あたり判定は一番悩みどころです。
今、必殺技とブロックのあたり判定の設定を行っています。必殺技に必要な変数
static int spstate; //必殺技の状態
static int spcnt = 0; //必殺技のカウント
static int sp2pos[3] = {0,0,0}; //必殺技の位置
static Rect2D sp1hit; //必殺技の当たり判定1
static Rect2D sp2hit[3]; //必殺技のあたり判定2
をstaticに変えました。
しかし、以前はblock.cppにもspstate,sp1hit,sp2hitを使っていました。
for(i = 0; i <13 ; i++){
for(j = 0; j < 9; j++){
//必殺技1とブロックとの当たり判定
if(HitRectAndRect(block1[i][j].hit,sp1hit)== TRUE && spflag == 1){
block1[i][j].life--;
if(block1[i][j].life == 0){
block1[i][j].flag = 1;
blocknumber++;
}
}
//必殺技3とブロックとの当たり判定
for(int k = 0;k<3;k++){
if(HitRectAndRect(block1[i][j].hit,sp2hit[k])== TRUE && spflag == 6){
block1[i][j].life--;
if(block1[i][j].life == 0){
block1[i][j].flag = 1;
blocknumber++;
}
}
}
}
}
}
char.cpp
void SpandBlockHit(BLOCK block1){
if(spstate == 1){
if(HitRectAndRect(block1.hit,sp1hit)== TRUE){
block1.life--;
if(block1.life == 0){
block1.flag = 1;
blocknumber++;
}
}
}
if(spstate == 6){
for(int k = 0;k<3;k++){
if(HitRectAndRect(block1.hit,sp2hit[k])== TRUE){
block1.life--;
if(block1.life == 0){
block1.flag = 1;
blocknumber++;
}
}
}
}
}
あと、
新・ゲームプログラミングの館のゲームプログラミングの設計では、それぞれ初期化のところで画像の読み込みを行うように
なっていたのですがload関数を作ってまとめて読み込むというやり方ではいけないのですか。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 15年前
- 住所: 東海地方
- 連絡を取る:
Re: ブロック崩し
当たり判定に関しては、デバッガで動作は確認されましたか?
それと、これだけのコードだと良く分かりません。
あとは、関係のあるモノ同士をまとめておかないとメンテナンス性が悪いです。
それと、これだけのコードだと良く分かりません。
それをするとload→Drawでグローバル変数になってしまうパターン多いからです。ヨシタケ さんが書きました:新・ゲームプログラミングの館のゲームプログラミングの設計では、それぞれ初期化のところで画像の読み込みを行うように
なっていたのですがload関数を作ってまとめて読み込むというやり方ではいけないのですか。
あとは、関係のあるモノ同士をまとめておかないとメンテナンス性が悪いです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ブロック崩し
申し訳ございません。データを下記のサイトにアップしました。パスワードは「YT2013」です。
プレイヤー用のソースはchar.cppと書きましたがplayer.cppに変えてあります。
ちなみにデバッグしてみてもよくわかりませんでした。
http://www1.axfc.net/uploader/so/2812273
今回の必殺技とブロックのようにあたり判定の場合、違うソースで設定したあたり判定をやり取りする必要があると思います。
ほかにも例えば必殺技のゲージ(spgauge)もブロックにあたったとき加算する必要があるのでplayer.cppとblock.cppで使います。
staticを使ってうまくやり取りするコツのようなものがあれば教えてください。お願いします。
プレイヤー用のソースはchar.cppと書きましたがplayer.cppに変えてあります。
ちなみにデバッグしてみてもよくわかりませんでした。
http://www1.axfc.net/uploader/so/2812273
今回の必殺技とブロックのようにあたり判定の場合、違うソースで設定したあたり判定をやり取りする必要があると思います。
ほかにも例えば必殺技のゲージ(spgauge)もブロックにあたったとき加算する必要があるのでplayer.cppとblock.cppで使います。
staticを使ってうまくやり取りするコツのようなものがあれば教えてください。お願いします。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 15年前
- 住所: 東海地方
- 連絡を取る:
Re: ブロック崩し
原因は分かりました。
void SpandBlockHit(BLOCK block1){
で受け取っているblock1は元のコピーに過ぎませんので、書き換えても元のblock.cppにあるblock1の内容は変更されません。
デバッガで値をちゃんと見ていれば、元のに反映されていない事に気づいたはずです。
これがC言語の引数の値渡と呼ばれるものですね。
【補足』 ポインタを使った参照渡しでも解決しますが私としては下記に書いたように今回は必要性を感じません。
私としては、SpandBlockHitと言う名前通り当たり判定だけに留めて元のblock.cppで消滅処理をすることをオススメします。
あとblock1.life==0が生存なのは齟齬を生みそうなので0と1の意味を逆にすることをオススメします。
あるいは、lifeが1以上で生存ならlifeだけで済ますほうがシンプルだと思います。
void SpandBlockHit(BLOCK block1){
で受け取っているblock1は元のコピーに過ぎませんので、書き換えても元のblock.cppにあるblock1の内容は変更されません。
デバッガで値をちゃんと見ていれば、元のに反映されていない事に気づいたはずです。
これがC言語の引数の値渡と呼ばれるものですね。
【補足』 ポインタを使った参照渡しでも解決しますが私としては下記に書いたように今回は必要性を感じません。
私としては、SpandBlockHitと言う名前通り当たり判定だけに留めて元のblock.cppで消滅処理をすることをオススメします。
あとblock1.life==0が生存なのは齟齬を生みそうなので0と1の意味を逆にすることをオススメします。
あるいは、lifeが1以上で生存ならlifeだけで済ますほうがシンプルだと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ブロック崩し
何度もアドバイスありがとうございます。
必殺技とブロックの当たり判定の関数をvoid型からbool型に変えて
判定されたときTRUEを返すようにして
block.cppに中身を というように変えたら正常に判定されるようになりました。
必殺技とブロックの当たり判定の関数をvoid型からbool型に変えて
bool SpandBlockHit(BLOCK block1){
if(spstate == 1){
if(HitRectAndRect(block1.hit,sp1hit)== TRUE){
return TRUE;
}
}
if(spstate == 6){
for(int k = 0;k<3;k++){
if(HitRectAndRect(block1.hit,sp2hit[k])== TRUE){
return TRUE;
}
}
}
return FALSE;
}
block.cppに中身を というように変えたら正常に判定されるようになりました。