2008/08/02
初級ゲームプログラミング完全マニュアル [vol.0057 2008/08/02]
┏┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳┓
┣┛ ┗┫
┃ 挫折不可能! 初級ゲームプログラミング完全マニュアル ┃
┃ ┃
┃ 第 57 号 2008/08/02 ┃
┣┓ ┏┫
┗┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻┛
- Ads space -
┌┬───────────────────────────────┬┐
││ はじめに ││
└┴───────────────────────────────┴┘
みなさま、こんにちは!
個人ゲーム制作アドバイザーの Byerkut です!
先日のことですが、ついにメルマガ読者が 700 名を突破いたしました!
これもひとえにみなさまのご支援のたまものと思っております。
日頃のご愛顧、まことに感謝感激しております。
今後もますます充実したメルマガをお送りできるよう、
日々是精進!一意専心!乾坤一擲!です。
さて、今回は当たり判定についてです。
┌┬───────────────────────────────┬┐
││本日のラインナップ ││
└┴───────────────────────────────┴┘
・今日のメインテーマ
【地球防衛戦線激化!当たり判定を極めろ!】
・考えようによっては、ためになるコラム
【本気の人のためのゲームプログラミング勉強法】
・あとがき
┌┬───────────────────────────────┬┐
││ みんなの備忘録 ││
└┴───────────────────────────────┴┘
■Visual C++ 2008 Express Edition をインストールする手順
http://www.game-create.com/archives/235
■Visual C++ 2008 Express Edition でプロジェクトを新規作成する手順
http://www.game-create.com/archives/270
■VC++ 2005 EE でプロジェクトにソースファイルを登録する手順
http://blog.mag2.com/m/log/0000240151/108824854.html
■画像ファイルを LoadImage() 関数で読み込めるようにする手順
http://www.game-create.com/archives/308
■ゲーム用ウィンドウのテンプレート
http://www.game-create.com/menu/downloads
■ビットマップ学習用クラス - Study::Bitmap
http://www.game-create.com/menu/downloads
┏┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳┓
┃┃ ┃┃
┃┃ 今日のメインテーマ ┃┃
┃┃ ┃┃
┃┃ 【地球防衛戦線激化!当たり判定を極めろ!】 ┃┃
┃┃ ┃┃
┗┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻┛
前回までのメルマガで地球防衛ゲームの敵キャラクターの作成と、
敵キャラクターの登場スケジュールを掌握することが
できるようになりました。これでプレイヤーと敵キャラクターが
動くようになりましたので、今回はお互いを戦わせてみましょう。
今回もサンプルソースを用意いたしましたのでダウンロードください。
http://www.game-create.com/archives/530
ダウンロードできたら実行してみてください。
敵キャラクターに攻撃が当たると、少しだけ動きが停止して、
さらに攻撃し続けると敵キャラを倒すことができるようになりました。
そうです、今回のテーマは「当たり判定」です。
まず、当たり判定とは何かというと「ふたつの物体が重なっているか?を
ふたつの物体の座標から判断すること」です。つまり、こちらの攻撃が
敵キャラクターに当たったか?を判定するというわけです。
実は、当たり判定というのは非常に奥が深く、当たり判定だけを扱った
8,000 円もする書籍が出版されるほどのテーマなのです。
このメルマガは初心者向けですので、その中でも一番簡単な、
「外接する矩形」での当たり判定をご紹介します。
「外接ってなんですか?」と思われるかと思います。
まずは次をご覧ください。
たとえば…
■
■
■■ ■■
■ ■
■ ■ ■ ■
■ ■
■ ■ ■
■■■■■■■
…という敵がいたときの外接する矩形とは…
┏━━━━━━━━━┓
┃ ■ ┃
┃ ■ ┃
┃ ■■ ■■ ┃
┃ ■ ■ ┃
┃■ ■ ■ ■┃
┃■ ■┃
┃■ ■ ■┃
┃ ■■■■■■■ ┃
┗━━━━━━━━━┛
…となります。つまり「中身を包んだ四角形」という意味です。
こうして物体と外接する四角形の座標どうしを使って、ふたつの物体が
重なっているかを判定していくのが今回の当たり判定です。
では、どうやってふたつの物体が重なっているかを
判定すれば良いのでしょうか?
これは実際に例を作って考えてみるのが簡単です。
たとえば、次のふたつの物体は重なっていますよね?
┏━━━━━━━━━┓
┃ ■ ┃
┃ ■ ┃
┃ ■■ ■■ ┃
┃ ■ ■ ┃
┃■ ■┏━━━━━━━━━┓
┃■ ┃ ■ ┃
┃■ ┃ ■ ┃
┃ ■■■┃ ■■ ■■ ┃
┗━━━━┃ ■ ■ ┃
┃■ ■ ■ ■┃
┃■ ■┃
┃■ ■ ■┃
┃ ■■■■■■■ ┃
┗━━━━━━━━━┛
これを式で表せばいいのです。
ちょっと図を簡単にしてみます。
┏━━━━━━━━━┓
┃A ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┏━━━━━━━━━┓
┃ ┃B │ ┃
┃ ┃ │ ┃
┃ ┃ │ ┃
┗━━━━┃────┘ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
これを式で表にはどうすれば良いのでしょうか?
まず、次の場合を考えてください。
┏━━━━━━━━━┓
┃A ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃ ┏━━━━━━━━━┓
┃ ┃ ┃B ┃
┃ 右 左 ┃
┃ 端 < 端 ┃
┗━━━━━━━━━┛ ┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
重なっていませんよね?
つまり、Aの右端よりBの左端の方が右にあるときは
「ふたつの物体は重なっていない」のです。「ふたつの物体が
重なっている」ための最初の条件は「Bの左端がAの右端よりも
左にあること」なのです。
┏━━━━━━━━━┓
┃A ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┏━━━━━━━━━┓
┃ ┃B │ ┃
┃ 左 右 ┃
┃ 端 < 端 ┃
┗━━━━┃────┘ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
次の条件は上下です。上下にも同じことが言えます。
┏━━━━━━━━━┓
┃A ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━下底━┛
<
┏━上底━━━━━━┓
┃B ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
Aの下底よりBの上底の方が下にある場合は「重なっていません」
第2の条件は「Bの上底がAの下底より上にあること」です。
┏━━━━━━━━━┓
┃A ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┏━上底━━━━━━┓
┃ ┃B │ ┃
┃ ┃ < │ ┃
┃ ┃ │ ┃
┗━━━━┃─下底─┘ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
こうやって上下左右を判定すれば「当たり判定」はバッチリそうですね。
しかし、実際にはこれでは不十分です。
上下左右を判定しているのになぜ不十分なのでしょうか?
次のケースを見てください。
┏━━━上底━━━━┓
┃B ┃
┃ ┃
┃ ┃
左 ┃
端 ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
┏━━━━━━━━━┓
┃A ┃
┃ ┃
┃ ┃
┃ 右
┃ 端
┃ ┃
┃ ┃
┃ ┃
┗━━━下底━━━━┛
この図は先ほどの「Bの左端がAの右端より左にあって、Bの上底が
Aの下底より上にある」という条件を満たしています。
しかし、見るからに重なっていませんよね?
そうです、さらに条件が必要なのです。
完全に重なっているかを判定するためには、前述のふたつの条件の上に
さらに「Aの左端がBの右端より左にあること」と
「Aの上底がBの下底より上にあること」が必要です。
┏━━━━━━━━━┓
┃B ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┏━上底━━━━━━┓
┃ ┃A │ ┃
┃ 左 < 右 ┃
┃ 端 端 ┃
┗━━━━┃─下底─┘ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━┛
これでふたつの四角形は「重なっている」ことになります。
まとめるとこういうことです。
「ふたつの四角形AとBにおいて次の条件を満たすとき、
ふたつの四角形AとBは重なっている。
・Bの左端がAの右端より左にある
・Bの上底がAの下底より上にある
・Aの左端がBの右端より左にある
・Aの上底がBの下底より上にある」
これをプログラムで判定している部分が collision_checks.cpp です。
collision_checks.cpp を開いてください。
------------------------------------------------------------------
collision_checks.cpp
------------------------------------------------------------------
#include "collision_checks.h"
void CheckCollisionPlayerBulletsAndEnemies(PlayerBullet *playerBullets, Enemy *enemies)
{
// 敵キャラを全部検索
for (int i = 0; i < ENEMIES_CONTROLLER_MAX; i++) {
// その中から生きている(=Alive)敵を見つける
if (enemies[i].status == Alive) {
// 弾を全部検索
for (int j = 0; j < ENEMIES_CONTROLLER_MAX; j++) {
// その中から撃たれている(=Emitted)弾を見つける
if (playerBullets[j].status == Emitted) {
// ここに到達したときは、
// 生きている敵と撃たれている弾が判明している
if (enemies[i].x < playerBullets[j].x + playerBullets[j].width &&
enemies[i].y < playerBullets[j].y + playerBullets[j].height &&
playerBullets[j].x < enemies[i].x + enemies[i].width &&
playerBullets[j].y < enemies[i].y + enemies[i].height) {
// 衝突したとき!!
playerBullets[j].status = Unuse; // ←弾を破壊する
enemies[i].life--; // ←敵のライフを減らす
enemies[i].y--; // ←少しノックバックさせる
if (enemies[i].life <= 0) { // ←もしライフが0なら破壊
enemies[i].status = Dead;
}
}
}
}
}
}
}
------------------------------------------------------------------
for 文によるループが二重になっていますが、これは「生きている敵と
撃たれている弾」を検索しているだけです。よくみると if 文で
敵のステータスが Alive のものと、弾のステータスが Emitted のものを
比較していることがわかります。
------------------------------------------------------------------
collision_checks.cpp
------------------------------------------------------------------
// 敵キャラを全部検索
for (int i = 0; i < ENEMIES_CONTROLLER_MAX; i++) {
// その中から生きている(=Alive)敵を見つける
if (enemies[i].status == Alive) {
// 弾を全部検索
for (int j = 0; j < ENEMIES_CONTROLLER_MAX; j++) {
// その中から撃たれている(=Emitted)弾を見つける
if (playerBullets[j].status == Emitted) {
// ここに到達したときは、
// 生きている敵と撃たれている弾が判明している
}
}
}
}
------------------------------------------------------------------
生きている敵と撃たれている弾がわかったら、先ほどの式を使って
ふたつの物体が「重なっているか?」を判定します。
それが次の if 文です。
------------------------------------------------------------------
collision_checks.cpp
------------------------------------------------------------------
// ここに到達したときは、
// 生きている敵と撃たれている弾が判明している
if (enemies[i].x < playerBullets[j].x + playerBullets[j].width &&
enemies[i].y < playerBullets[j].y + playerBullets[j].height &&
playerBullets[j].x < enemies[i].x + enemies[i].width &&
playerBullets[j].y < enemies[i].y + enemies[i].height) {
// 衝突したとき!!
playerBullets[j].status = Unuse; // ←弾を破壊する
enemies[i].life--; // ←敵のライフを減らす
enemies[i].y--; // ←少しノックバックさせる
if (enemies[i].life <= 0) { // ←もしライフが0なら破壊
enemies[i].status = Dead;
}
}
------------------------------------------------------------------
enemies をA、 playerBullets をBとおいて先ほどの式と
照らし合わせてみてください。 x は左端、 y は上底、
x + width は右端、 y + height は下底です。
もし、この式によって敵と弾が重なっていることがわかった場合は、
次のようにして敵キャラのライフを減らしています。
さらに敵キャラのライフが0になった場合は、その敵キャラを
破壊(=Dead)しています。弾は必ず破壊するようにします。
なぜかというと、弾を破壊しないと貫通弾になってしまうからです。
------------------------------------------------------------------
collision_checks.cpp
------------------------------------------------------------------
// 衝突したとき!!
playerBullets[j].status = Unuse; // ←弾を破壊する
enemies[i].life--; // ←敵のライフを減らす
enemies[i].y--; // ←少しノックバックさせる
if (enemies[i].life <= 0) { // ←もしライフが0なら破壊
enemies[i].status = Dead;
}
------------------------------------------------------------------
さて、今回はゲームプログラミングの重要項目である「当たり判定」を
とりあげることができるようになりました。
これもみなさまの成長のたまものです。
もし、何かわからないことがありましたら
お問い合わせフォームなどからお気軽に質問してください。
来週は敵キャラクターの侵入を許してしまった場合のペナルティについて
実装していきたいと思います。
今回も最後まで読んでいただきましてありがとうございます!
それでは!
Byerkut.
┌┬───────────────────────────────┬┐
││ 考えようによっては、ためになるコラム ││
││ ││
││ 【本気の人のためのゲームプログラミング勉強法】 ││
└┴───────────────────────────────┴┘
いつもゲーム制作について数少ない貴重な情報を発信されている
どり〜むきっずの早瀬さんより新しい教材の発表がありました。
本当のゲームプログラマになるためのステップについて綴られています。
世の進化はとても早いです。 32bit 機を出したかと思ったら今度は
64bit 機、最近では CPU が8個とか、もうわけがわかりません。
古い技術は捨てられていきます。新しい技術は今日も生まれています。
このような状態で常に自分の想像するゲームを作り続けられる人こそ
本当のゲームプログラマ−と言えるでしょう。
こんな話があります。某有名な RPG を作っている会社に二人の学生が
入社面接に訪れたそうです。一人は PS2 を使ってバリバリと開発を
していることをアピールしました。実際に PS2 で動く作品も
持参しました。もう一人の方は作品の持参はありませんでした、
そればかりか PS2 での開発経験もありませんでした。
しかし、受かったのは後者の人でした。なぜでしょうか?
その答えがここにあります。
http://dreamkids.sakura.ne.jp/info/002.html
ゲームプログラマとしての自分磨きにピリリと辛い、
でも確かに効いてる隠し味。お試しあれ。
Byerkut.
┌┬───────────────────────────────┬┐
││ 重要記事のダイジェスト ││
└┴───────────────────────────────┴┘
■C++ は計算と記憶しかできない
■外部の機能(関数)を借り受けるには #include を使う
■#include で借り受けた機能を使うには関数を呼び出す
http://archive.mag2.com/0000240151/20080203171350000.html
■Windows プログラムには必ず WinMain() 関数から開始する
http://archive.mag2.com/0000240151/20080209215231000.html
■プログラムは関数の集まり
■プログラムは関数を増やしてソフトウェアを作る
■関数には次の要素がある(名前・機能・引数・戻り値)
http://archive.mag2.com/0000240151/20080216090000000.html
■関数を作には次の要素を決める(名前・機能・引数・戻り値)
http://archive.mag2.com/0000240151/20080608200909000.html
■プログラムはソースコードを分散させて大きなソフトを作る
http://archive.mag2.com/0000240151/20080525121425000.html
http://archive.mag2.com/0000240151/20080531212858000.html
■ゲームを作るためには「入力」「出力」「条件分岐」が必要
http://archive.mag2.com/0000240151/20080301221623000.html
■画像ファイルの絵を画面に表示する際にはメモリに読み込む
http://archive.mag2.com/0000240151/20080406202024000.html
■LoadImage() 関数はコピペで使い回せる
■LoadImage() 関数の第2引数は
メモリに読み込みたい画像ファイルの名前を渡す
■LoadImage() 関数で読み込むファイルは
プロジェクトディレクトリにおいてある必要がある
http://archive.mag2.com/0000240151/20080413174135000.html
┌┬─────┬─────────────────────────┬┐
││ あとがき │ 発行者のつぶやきです ││
└┴─────┴─────────────────────────┴┘
桐生第一、出場できるみたいですね。
良かったのやらそうでないのやら…
┌┬───────────────────────────────┬┐
├┘ └┤
│ 購読の解除はこちら↓ │
│ http://www.game-create.com/contents/gp_beginners_ml │
├┐ ┌┤
└┴───────────────────────────────┴┘
- Ads space -
┌┬───────────────────────────────┬┐
├┘ └┤
│ 「挫折不可能!初級ゲームプログラミング完全マニュアル」は、 │
│ 「いちばんやさしいゲームの作り方」が運営しています。 │
│ │
│ サイト : http://www.game-create.com/ │
│ 発行元 : http://www.game-create.com/menu/about │
│ 発行者 : http://www.game-create.com/menu/profile │
│ 問い合わせ : http://www.game-create.com/menu/contact │
│ 利用規約 : http://www.game-create.com/informations/agreement │
├┐ ┌┤
└┴───────────────────────────────┴┘
POWERED BY LIBERTIASTER GAME STYLE.


