2008/09/03
初級ゲームプログラミング完全マニュアル [vol.0061 2008/09/03]
┏┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳┓
┣┛ ┗┫
┃ 挫折不可能! 初級ゲームプログラミング完全マニュアル ┃
┃ ┃
┃ 第 61 号 2008/09/03 ┃
┣┓ ┏┫
┗┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻┛
- Ads space -
┌┬───────────────────────────────┬┐
││ はじめに ││
└┴───────────────────────────────┴┘
みなさま、こんにちは!
個人ゲーム制作アドバイザーの Byerkut です!
9月に入りましたが、暑かったり、肌寒かったり、ラジバンダリです。
先日のことですが、なんと当ブログが Google で「ゲーム 作り方」の
検索結果で1位になることができました。
これも日頃、みなさまにご愛顧いただいているおかげです。
本当にありがとうございました!
これからもためになる情報発信を信念にがんばります!
さて、今回は地球防衛隊員のまどかに最強の敵キャラが挑みます。
┌┬───────────────────────────────┬┐
││本日のラインナップ ││
└┴───────────────────────────────┴┘
・今日のメインテーマ
【敵母艦襲来!戦えまどか!明日を取り戻すんだ!】
・あとがき
┌┬───────────────────────────────┬┐
││ みんなの備忘録 ││
└┴───────────────────────────────┴┘
■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/673
ダウンロードできましたら画像ファイルをプロジェクトディレクトリに
コピーして遊んでみてください。しかも本気で。
最後にボスキャラクターが出てきますので倒してみましょう。
油断してはいけませんよ?ボスは敵キャラクターを吐いてきます。
もちろん、ボスが吐いた敵キャラクターが陣地に落ちてもダメージを
受けますので注意してください。
…さて、いかがでしたでしょうか?
ボスキャラクターらしいしぶとさがありましたよね。
ちなみに 1000 発打ち込まないと倒せないようになっています。
このクソゲーっぽい数値設定も好きな人にはたまらないと思います。
さて、早速このボスキャラクターの裏側をお伝えしていくのですが、
実は、ボスキャラクターと言っても今回のボスキャラクターは
雑魚敵となんら変わりはありません。実際に enemies.cpp を開いて
みてください。 Enemy001_* とよく似た Boss_* という関数があります。
------------------------------------------------------------------
enemies.cpp
------------------------------------------------------------------
void Boss_Initialize(Enemy &enemy)
{
// ボスの初期化
}
void Boss_Update(Enemy &enemy, HWND hWindow)
{
// ボスの更新(移動など)
}
void Boss_Draw(Enemy &enemy, HDC hBackBuffer)
{
// ボスの描画
}
------------------------------------------------------------------
ということは、ボスキャラクターが登場するタイミングも雑魚敵と同じ
なのでしょうか? enemies_controller.cpp を見てください。
------------------------------------------------------------------
enemies_controller.cpp
------------------------------------------------------------------
static EnemiesTimeline enemiesTimeline[] = {
100, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw,
200, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw,
300, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw,
400, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw,
500, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw,
600, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw,
1000, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw,
1100, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw,
1200, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw,
1300, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw,
1400, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw,
1500, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw,
2000, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw,
2100, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw,
2200, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw,
2300, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw,
2400, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw,
2500, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw,
3000, Boss_Initialize, Boss_Update, Boss_Draw,
};
static int frame = 0;
------------------------------------------------------------------
注目すべきは最後の1行です。
------------------------------------------------------------------
3000, Boss_Initialize, Boss_Update, Boss_Draw,
------------------------------------------------------------------
どうやら、雑魚敵と同じように時間が来たら登場するようですね。
そうです、今回の場合は 3000 フレームが経過したらボスキャラクターが
登場します。今回は簡略化のためボスキャラクターと雑魚キャラクターを
同じような枠組みで使っていますが、実際のゲームでは全く別の
プログラムが動くようなケースも珍しくありません。
というかむしろそっちの方が多くなるはずです。
本格的なボスキャラクターの作り方はタスクシステムを勉強してから
取り扱いたいと思いますので今しばらくお待ちください。
今回のボスキャラクターは雑魚敵と同じ枠組みで動いていることが
わかりましたが、同じと言っても明確に違う部分がひとつあります。
そうです、ボスキャラクターは雑魚敵を吐いてきます。
この雑魚敵を吐くという行動はボスだけのものなのです。
では、ボスキャラクターに雑魚敵を吐かせるためにはどうしたら
良いのでしょうか?
難しそうと思われましたか?
でも冷静に考えてみてください。
雑魚敵を画面に登場させる方法はすでに組んであるのです。
ゲームが始まると敵キャラが上から降ってきますよね?
この「上から降ってくる」を「ボスから降ってくる」に変えられれば
目的を達成することができます。
つまり、ボスキャラクターから「上から雑魚敵が降ってくる」という
一連の処理を呼び出すことができればいいのです。
この「呼び出す」という単語が出てきたら関数を思い出してください。
プログラムでは「処理」を「呼び出す」という表現が出てきたら
「関数を使う」ということなのです。
では、「上から雑魚敵が降ってくる」という処理は
どこにあるのでしょうか?わかったらすごいと思います。
答えは enemies_controller.cpp です。
UpdateEnemiesController() 関数の中をご覧ください。
------------------------------------------------------------------
enemies_controller.cpp
------------------------------------------------------------------
// タイムスケジュールに従って敵キャラを生成する
if (current < (sizeof(enemiesTimeline) / sizeof(EnemiesTimeline))) {
while (enemiesTimeline[current].frame == frame) {
CreateEnemy((std::rand() % ENEMIES_SCREEN_WIDTH) - 32, -16,
enemiesTimeline[current].initializer,
enemiesTimeline[current].updater,
enemiesTimeline[current].drawer);
current++;
}
}
------------------------------------------------------------------
お、よく見ると CreateEnemy() といういかにも目的が達成できそうな
関数があるではありませんか。これを使えば簡単にボスキャラクターから
雑魚敵を生成することができそうですね。
(ちなみに前回まではここが関数化されていませんでした、
設計のミスですので、申し訳ありませんが忘れてください)
この CreateEnemy() という関数は、第1引数と第2引数に
生成する敵キャラクターの初期座標を設定し、第3第4第5引数に
それぞれ、初期化関数、更新関数、描画関数を指定します。
前述の例ではいろいろと計算していますが、これは敵キャラクターを
ランダムな位置に登場されるための計算です。興味のある人は
インターネットで調べてみましょう。当サイトにもヒントがあります。
さて、これですべての用意が調いました。早速ボスキャラクターに
雑魚キャラクターを吐かせてみましょう。
…と、意気揚々と挑むのは良いのですが、どこに CreateEnemy() 関数を
書けばいいのかわかりますでしょうか?
実はいろんなところに書けるのですが、もっとも適切なのは
enemies.cpp の Boss_Update() 関数の中です。
この関数は敵の初期化と描画以外のすべての処理を担当するため、
雑魚敵を吐くという処理はここがもっとも適しているのです。
ちなみになぜ更新と描画の関数が分かれているのかというと、
処理落ちが発生したときに描画処理のみをスキップするためです。
関数が分かれていないと、どこからどこまでをスキップすればいいのか
わからないというわけです。
Boss_Update() 関数のなかに CreateEnemy() 関数を書けば良いという
ことがわかりました。では具体的にどう記述すればいいのでしょうか?
たとえば Enemy001_* に対応する敵キャラクターを吐かせたい場合は…
------------------------------------------------------------------
enemies.cpp
------------------------------------------------------------------
CreateEnemy(enemy.y, enemy.y, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw);
------------------------------------------------------------------
…となります。
生成する敵キャラクターの初期座標に自分のX座標とY座標を入れている
点に注目してください。自分が今いる位置から敵キャラクターを登場
させることで、敵キャラクターを「吐かせる」という行動を実現します。
同様に Enemy002_* と Enemy003_* の敵キャラクターを
吐かせたい場合は…
------------------------------------------------------------------
enemies.cpp
------------------------------------------------------------------
CreateEnemy(enemy.y, enemy.y, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw);
CreateEnemy(enemy.y, enemy.y, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw);
------------------------------------------------------------------
…となります。
実際に書く場合は乱数などを使って 100 フレームに1回くらいの
ペースで敵キャラクターを生成するようにしてください。
もし1フレームごとに敵キャラクターを生成してしまうと、
1秒間に 60 体も敵キャラが登場してしまい、ゲームになりません。
ちなみに Byerkut さんはここをどうやって書いたかというと…
------------------------------------------------------------------
enemies.cpp
------------------------------------------------------------------
void Boss_Update(Enemy &enemy, HWND hWindow)
{
int x = static_cast<int>(sin(static_cast<float>(enemy.time % 360) / 180 * 3.141592f) * 240);
enemy.x = x + 320 - 120;
enemy.y++;
// 1/100 の確率で敵を吐く
if ((rand() % 100) == 0) {
int x = enemy.x + ((240 - 16) / 2);
switch (rand() % 3) {
case 0:
CreateEnemy(x, enemy.y + 100, Enemy001_Initialize, Enemy001_Update, Enemy001_Draw);
break;
case 1:
CreateEnemy(x, enemy.y + 100, Enemy002_Initialize, Enemy002_Update, Enemy002_Draw);
break;
case 2:
CreateEnemy(x, enemy.y + 100, Enemy003_Initialize, Enemy003_Update, Enemy003_Draw);
break;
}
}
}
------------------------------------------------------------------
…となっています。
if ((rand() % 100) == 0) ということで 1/100 の確率で
敵キャラクターを生成していることになります。
この計算の意味は「ランダムな数値が 100 で割り切れたときに」です。
つまり、適当な数値をコンピュータに決めてもらい、その数値が 100 で
割り切れたときに敵キャラクターを生成するということをやっています。
さらにその下に計算がありますが、これもランダムで3種類の敵の
どれかを生成するということをやっています。
それ以外にも計算をやっていますが、これらは座標の位置を調整するための
ものですので、最初はわからなくてもかまいません。
わかったところでプログラミングの腕は上達しませんので、
気にしないのが良いと思います。
さて、これでボスキャラクターが雑魚敵を吐くという仕様の全貌が
明らかになりました。このあたりの数値を調整することで、
ゲームの難易度を簡単にしたり難しくしたりということが
できるようになります。
では、次回は最終調整をしてゲームとして完成させてしまいましょう。
いよいよ地球防衛戦線の最終決戦となります!
今週も最後まで読んでいただきましてありがとうございます!
それでは!
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.


