C言語 入門 STGの作り方

八日目 当たり判定

初心者向けSTG作成入門

HOME/STGの作り方 目次/八日目 当たり判定/

広告

↓2016年02月29日発売↓

12歳からはじめる ゼロからのC言語 ゲームプログラミング教室

新品価格
¥2,462から
(2016/5/10 22:16時点)

もっと!C、C++言語本

目次へ戻る

当たり判定

今回は当たり判定をつけてみます。

使い方はけっこう簡単なので先にプログラムから紹介します。

業界では有名な当たり判定だそうです。

プレイヤーや敵の構造体にあった当たり判定範囲「range」と体力を表す「power」を使います。

void my_init_player(){
	p1.x = 0;
	p1.y = -200;
	p1.draw_x = 0;
	p1.draw_y = 0;
	p1.speed = 4;
	p1.shot_type = 0;
	p1.power = 100;
	p1.life = 3;
	p1.range = 10;
}

プレイヤーの時は体力「power」を「100」当たり判定範囲「range」を「10」に決めましたがこのへんは好みで微調整してください。

当たり判定プログラム

では当たり判定プログラムを見てみましょう。

まずはプレイヤーと敵がお互い体当たりした場合の当たり判定です。

お互いの距離と当たり判定の範囲を入れる為の変数「double x,y,range」を用意した上で、

double x,y,range;
x = p1.x - enemy[i].x;
y = p1.y - enemy[i].y;
range = p1.range + enemy[i].range;
if (x * x + y * y < range * range){
	p1.power--;
	enemy[i].power--;
}

こちらが当たり判定プログラムになります。

思ったより難しそうですね・・・。

計算が見慣れてないとちょっとややこしそうですが一つ一つ見ていけばやってる事はスゴく簡単なので心配しないでください。

まずはお互いの距離を調べます。

x = p1.x - enemy[i].x;
y = p1.y - enemy[i].y;

この部分ですね。

お互いの距離といっても直線ではなく「x」と「y」の距離をそれぞれ出します。

上記の計算では「プレイヤー 引く 敵」になってますが「敵 引く プレイヤー」でも大丈夫です。

次にお互いの当たり判定の範囲「range」を足します。

range = p1.range + enemy[i].range;

こちらですね。後はこの3つ「x」「y」「range」を使って計算するだけです。

if (x * x + y * y < range * range){
	/*当たっています!!!*/
}

「「x」の2乗 + 「y」の2乗 が 「range」の2乗より小さければ」

お互いの当たり判定範囲が重なってる、ぶつかっているという事になるので

お互いの体力「power」を減らせば当たり判定処理完了です。

当たり判定部分を関数に

このお互いの距離、当たり判定範囲を比べる計算は何度も登場するので関数にしてしまいます。

int my_pythago_theorem(double x,double x2,double y,double y2,
	double range,double range2){

	double x3 = x - x2;
	double y3 = y - y2;
	double range3 = range + range2;
	if (x3 * x3 + y3 * y3 < range3 * range3){
		return 1;
	}
	else{
		return 0;
	}

}

ちょっと引数が多いですがさきほどの計算に使ったそれぞれのx座標、y座標、当たり判定範囲を渡して同じように計算しているだけです。

当たっていれば「return 1」、そうでなければ「return 0」を返します。

「my_pythago_theorem」ピタゴ・・・なんちゃらとか名付けられておりますね・・・。

そうです!この当たり判定にはかの有名なピタゴラスの定理が使われております。

このへんについては余談として後ほどまた説明したいと思います。

それぞれの当たり判定

それでは個別の当たり判定がわかったトコロで今度はそれぞれのショットなどにも当たり判定を加えてみます。

まずはプレイヤーと敵、プレイヤーショットと敵についての当たり判定になります。

当たり判定関数の「my_collision_detection()」の前半部分です。

最初に動いている「enemy[i].move_flag == 1」かつ初期化済み「enemy[i].init_flag == 1」の敵を探します。

for(int i = 0;i < DISP_ENEMY_MAX;i++){
 if(enemy[i].move_flag == 1 && enemy[i].init_flag == 1){
 }
}

そしてそれぞれに当たり判定を加えていきます。

まずはプレイヤーと敵の当たり判定です。

if(my_pythago_theorem(p1.x,enemy[i].x,
	p1.y,enemy[i].y,p1.range,enemy[i].range)){

	p1.power--;
	enemy[i].power--;

}

こちらはさきほどやったので大丈夫ですね。

「my_pythago_theorem()」にそれぞれ引数を入れてそれが当たっている「return 1」を返された時にそれぞれの体力を減らしているだけです。

次にプレイヤーショットと敵の当たり判定です。

プレイヤーショットは一体だけではないので少しややこしくなります。

for(int j = 0;j < PLAYER_SHOT_MAX;j++){
 if(ps1[j].move_flag == 1 && ps1[j].init_flag == 1){
 }
}

まず同じように動いている「ps1[j].move_flag == 1」かつ初期化済み「ps1[j].init_flag == 1」のプレイヤーショットを探します。

そしてここからさらにそのショットの最大弾数分、「ps1[j].flag[k] == 1」最初に少しだけ説明した例の「ps1[].flag[]」がオンのショットを探します。

for(int k = 0;k < ps1[j].max_bullet;k++){
	if(ps1[j].flag[k] == 1){
	}
}

さてこれがなんなのかと言いますと次の部分を見てください。

if(my_pythago_theorem(enemy[i].x,ps1[j].x[k],
 enemy[i].y,ps1[j].y[k],enemy[i].range,ps1[j].range)){

  enemy[i].power--;
  ps1[j].flag[k] = 0;

}

ようやくここで当たり判定です。

同じように「my_pythago_theorem()」に引数を渡して当たり判定があれば「enemy[i].power--」と敵の体力を減らしております。

ここまでは想像通りの処理だと思いますが次の「ps1[j].flag[k] = 0;」で例のフラグを落としております。

前回までにやった内容だともしプレイヤーショットが敵に当たって、その後消滅させるとしたら、

ps1[j].move_flag = 0;
ps1[j].init_flag = 0;

こちらの2つのフラグを落としそうなものですよね。

しかし落としているのは「ps1[j].flag[k] = 0;」こちらのフラグだけです。

これで何が変わるかと言えばまずこちらのフラグを使ってプレイヤーショットは表示されているのでその弾は表示されなくなります。

同じく当たり判定にもこのフラグを使っているので当たり判定もされなくなります。

つまり見た目上はその弾は消滅します。

それではなぜこんなめんどくさい方法をとっているかの最大の理由ですが、たとえば一度に5方向に弾を飛ばすショットを作った時などに、今回の作り方だと「プレイヤーショットの入れ物」一つで5方向の弾の座標を持つ事になるのでさきほどの

ps1[j].move_flag = 0;
ps1[j].init_flag = 0;

これを使ってその「プレイヤーショットの入れ物」そのもののフラグを落としてしまうと当たった弾だけではなくその他4つの弾も一気に消滅してしまう事になってしまうのです!

なので5発分のフラグを用意して弾が当たったところだけフラグを落として見た目上弾を消滅させているというワケです。

それでは話を戻しましてあとは

if(enemy[i].power <= 0){
	enemy[i].move_flag = 0;
	enemy[i].init_flag = 0;
}

敵の残りpowerを見て敵を消滅させればオッケーです。

プレイヤーについてはここでは体力がマイナスになってもそのままです。

のちほどステージ遷移などと一緒にそのへんの処理を加えていきます。

残るは敵のショットの当たり判定ですがほとんど同じような処理になります。

for(int i = 0;i < DISP_ENEMY_MAX * 2;i++){
 if(enemy_shot[i].move_flag == 1 && enemy_shot[i].init_flag == 1){
 }
}

この時に

for(int j = 0;j < enemy_shot[i].max_bullet;j++){
 if(my_pythago_theorem(p1.x,enemy_shot[i].x[j],
  p1.y,enemy_shot[i].y[j],p1.range,enemy_shot[i].range)){

  p1.power--;

 }
}

最大弾数分調べるだけです。

画像(s-8-1)

当たり判定完了!!!

ピタゴラスの定理

知っている方も知らない方も。

ではここからはなぜ上の方法で当たり判定がわかるのか?をお話します。

ピタゴラスの定理

例えば次の場合のプレイヤーと敵との直線距離

画像(s-8-2)

これを知りたい時はピタゴラスの定理(三平方の定理)を使います。

これは次の直角三角形があったとして

画像(s-8-3)

それぞれの長さの関係は

「底辺の2乗 + 高さの2乗 = 斜辺の2乗」

こんな関係になりますよってのが「ピタゴラスの定理」ってものになります。

そういえば、これどこかで見た気がしますよね!

それぞれ長さを次のように置き換えて

画像(s-8-4)

プログラム式に書き換えてみると

「x * x + r * r = range * range」

正しいプログラムではないですが、今までやっていた計算みたいになりました!

この事から今までやっていた計算はまさしくお互いの直線距離を表すものだったのです。

それでいて例えば今から会いに行きたい友達が100メートル離れた場所にいたとして、その友達が半径100メートルの少し太った方だともうすぐに会えますよね。

つまりはその二人の当たり判定範囲分を合わせた分

画像(s-8-5)

これが実際の二人の距離を超えちゃった場合

if (x * x + y * y < range * range)

この場合が衝突してるという事になるんですね!

そもそもピタゴラスの定理がわからない!という方は近くの数学に詳しい方に聞いてください。

ここまでの中間ソースになります。

中間ソース7

次回は爆発エフェクトを作ります。

次回

九日目 爆発エフェクト

□ページの先頭へ□

□HOME□

広告

↓2014年06月20日発売↓

14歳からはじめるC言語わくわくゲームプログラミング教室 Visual Studio 2013編

新品価格
¥2,500から
(2016/5/10 22:17時点)

↓2014年10月25日発売↓

超本格! サンプルで覚えるC言語 3Dゲームプログラミング教室

新品価格
¥3,110から
(2016/5/10 22:18時点)

↓2013年07月25日発売↓

小学生からはじめるわくわくプログラミング

新品価格
¥2,052から
(2016/5/10 22:21時点)

↓2016年05月13日発売↓

小学生からはじめるわくわくプログラミング2

新品価格
¥2,052から
(2016/5/10 22:22時点)

もっと!C、C++言語本

□ページの先頭へ□

□HOME□