yaneuraoGameScript2000で行こう! |
written by やねうらお
1.ygs2kにはもう慣れたかな〜?
前号・前々号でご紹介したyaneuraoGameScript2000(以下ygs2k)はどうでしたか?もうゲームプログラムは組めるようになりましたか?今回は、ygs2kでさらに高度なプログラムを組んで行きましょう。心配いりません。最初はみんな初心者なのです。少しずつサンプルを改造したりしているうちに覚えるんです。幸いにして、ygs2kのサンプルは豊富に用意されていますので、心配はいりません。今回からこの講座を読み始められた方も、C言語の入門書等を併用すればなんとか追いつけますので、頑張ってみてくださいね。
それではまず、ygs2kの最新版を用意してください。(クリックしてファイルに保存してください。使い方は前の号を参考にしてください) 一応、私のホームページからもダウンロードすることが可能です。
私は、ygs2kは、これで最終版にするつもりでいます。いまは、次回作(『Revolution』 WAFFLE企画・販売。9月29日発売予定)のために、少し、書き直していますが…。
画面は『Revolution』のもの。このゲームもまた、ygs2kで作られています。
ygs2k、あるいはプログラミングの持つ無限の可能性を感じていただければ幸いです。
今回は、この『Revolution』のキャラクターを実際に読者のみなさんと動かしてみることにしましょう。
2.ゲームに必要なものはこれだ!
ゲームに必要なプログラミングの知識とは何でしょうか?簡単なシューティングを例にとって考えてみましょう。
1.キャラクターを表示する
2.マイキャラはユーザーにキー入力に応じて動かす
3.敵キャラも動かす
4.マイキャラと、敵や弾とが接触していればゲームオーバー
ではないですか?難しいですか?簡単ですよね。それでは、これをさらに分解して考えてみます。
1.のキャラクター表示とは何ですか?
まず、シューティングで表示しなくてはならないキャラクターとしては、マイキャラ(自機)、そして自分の出した弾、そして敵とこの3つが思い浮かびますね。ここでは、話を簡略化するため、いま、弾は無しにしてマイキャラと敵キャラの2つについて考えましょう。
マイキャラを表示するとは、どういうことでしょうか?それは、マイキャラを画面に転送すれば良いのです。マイキャラは画像ですから、あらかじめビットマップファイルから(プレーンに)読み込んでおき、それを画面に転送してやれば良いのです。これは、前回もやりましたね。まずは、このビットマップ(クリックしてファイルに保存してください)のキャラクターを動かしてみましょう。
まず、YGS2000.exeの本体のあるフォルダにこのビットマップをコピーします。そして、scriptというフォルダを作って、そのフォルダ内にgamestart.txtというテキストファイルを作って、そこにプログラムを書いていき、それを保存したらYGS2000.exeを実行すれば良いのでしたね。
void main(){ // 1 int n; // 2 LoadBitmap("ash.bmp",0,1); // 3 SetFPS(10); // 4 n = 0; // 5 loop { // 6 ClearSecondary(); // 7 BltRect(0,100,200,n*120,0,120,82); //8 n = n + 1; // 9 if (n==5) n = 0; // 10 halt; // 11 } // 12 } //13 |
とりあえず、私がプログラム書いてみたんで、何も考えずにこれを実行してみましょう。赤字の部分は、あとで解説するために付けたのです。入力しなくても構いませんし、//はその行のそこ以降はコメント扱いになりますので入力しても動作に支障はありません。
上の画面のようにキャラクターが出てきて、動きましたか?動いたならば、まずは成功です。動かなかった人は、入力ミスが無いかチェックしてみましょう。
えっと。プログラムの解説は前回と重複する部分があるのですが、前回と重複する部分も簡単に説明します。
1と13はおまじないですね。ここがプログラムだよって知らせの合図と、終了の合図。{ と }は一つのブロックを表すのに使うのでしたね。
6も前回やりました。ぐるぐる回るための構文(永久ループ)でしたね。この{は、12の}と対応するので、12まで行ったら、また6に戻ってくるわけです。
2は前回やった変数宣言ってやつですね。nという変数(数の入れられる箱)を宣言(使うよって事前にコンピュータに教えてやること)しているのですね。5では、そのnという変数に0を代入しています。9ではnに1を足して、10ではnが5になっていれば、nを0に戻します(これも前回やりましたね) これは、キャラクターが、5つのモーションから成るからで、nは0,1,2,3,4という数字をとればそれでいいからです。
7は画面を消す命令ですね。なぜこれが必要ですか?そういう疑問が出たときは、この部分をコメントにして再度実行してみましょう。コメントにするには//が便利です。
// ClearSecondary();
とやって確認してみてください。(このようにコメント化して無効な行にすることをコメントアウトする、と言います。一時的にコメントアウトしたいときには//が便利です) 画面を消さないものだから、前回の残像がずっと残りますね。(これはこれで面白いのですが)
3は0番のプレーンに読み込む命令、8は0番のプレーンから、セカンダリプレーン(裏画面)に転送する命令。11は、セカンダリプレーンとして裏でこっそり描画していたものを一気にプライマリプレーン(表画面)に転送する命令ですね。なぜこんなことが必要なのかというと、描いている手順がユーザーに見えると困ることがあるので、常に描画はセカンダリプレーンに対して行なっているのです。8については、カンマで区切って数字が並んでいますが、それぞれの意味については、manualフォルダにあるスクリプトマニュアルのほうを参照していただくことにしましょう。
あと、残るは4ですね。これは、秒間のフレーム数を決める命令です。実は、haltのときに時間待ちを行なっているのです。そうしないと、速いマシンだととてつもなく速くキャラクターが動いてしまうので。初期状態では1/60秒の時間待ちを行なっています。つまりhaltは最大でも一秒間に60回しか実行できないのです。最大フレーム数60フレーム/秒。このフレーム/秒は、Frames Par Secondの頭文字をとって、FPSとも言います。この場合60FPSなわけです。このFPSを調整するのがSetFPSという命令なのです。
試しに、コメントアウトしてみましょう。60FPSで動くので、さきほどの6倍の速度。5フレームで1回剣を振るので、60FPSならば秒間12回も剣を振るのです。振りすぎですね(笑)
3.アニメーション
前の章でもう気づかれたと思いますが、キャラクターアニメーションとは、同じ座標に対して時間経過とともに少しずつ異なる絵を転送してやることによって実現します。パラパラ漫画とかの要領ですね。
void main(){ int n; LoadBitmap("ash.bmp",0,1); SetFPS(10); n = 0; loop { if (n!=0) n = n + 1; // nが0でないならば1を足す if (n==5) n = 0; // nが5ならば0にする KeyInput(); // キー入力を行なう if (IsPushSpaceKey()) n = 1; // スペースキーが押されていれば、nを1にする ClearSecondary(); BltRect(0,100,200,n*120,0,120,82); halt; } } |
少しプログラムを変えました。これを実行するとどうでしょうか?
スペースキーを押すごとに一回ずつ剣を振れますね。不思議ですね。新しい命令はキー入力の部分しか出てきていないので、難しくは無いと思います。さきほど、nは0,1,2,3,4を循環するような役割を持っていましたが、今度は少し違うのです。今度は、(実際にプログラムの流れを目で追っていただければわかると思いますが)最初は0で、ユーザーがスペースを押したとき1になり、そのあと、2,3,4という値をとって、0にまた戻るという役割を持っています。
つまり0から1になるのに、ユーザーがスペースを押すというトリガーが必要で、いったん1になれば、そのあと2,3,4と(自然に)増加して、また0に戻ると。
このように数を扱うはずの変数でも、その前後の文脈で、さまざまな意味を担うことがあります。実はこれがプログラミングを複雑に、そして他人のプログラムを理解しにくくしている原因なのかも知れません。これは、プログラミングの一大テーマでしょう。(いま考えても仕方ないのかも知れません)
さて。キー入力が出来たところで、キャラクターの移動もさせてみましょう。
void main(){ int n,x,y; LoadBitmap("ash.bmp",0,1); SetFPS(10); n = 0; x = 100; y = 100; loop { if (n!=0) n = n + 1; if (n==5) n = 0; KeyInput(); if (IsPushSpaceKey()) n = 1; if (IsPressUpKey()) y = y - 8; if (IsPressDownKey()) y = y + 8; if (IsPressLeftKey()) x = x - 8; if (IsPressRightKey()) x = x + 8; ClearSecondary(); BltRect(0,x,y,n*120,0,120,82); halt; } } |
赤い部分が追加・変更箇所です。キャラクターを転送する座標をxとyという変数で持っておき、矢印キーの入力に応じて、その変数の値を変更してやることで移動しているわけです。たとえば
if (IsPressUpKey()) y = y - 8;
ならば、これは上矢印が押されていればyから8を引きなさいということです。その結果、キャラクターは8ドット分だけ上に表示されるようになると。結果として、上に移動したように見えるのです。
実際に実行して、上矢印と左矢印というように同時押しによって斜めに移動できることを確認してください。そして、移動しながらスペースキーで剣を振れることを確認してください。
そのほか、y = y - 8の8という数字を変更すればどうなるかだとか、そういうのをいろいろ試行錯誤してみると、より理解が深まると思います。
4.敵キャラを動かす
マイキャラの表示・移動は終わったので、次に敵キャラを動かすことを考えてみましょう。敵キャラもアニメーションさせる場合は、さきほどのマイキャラと同じように、現在何番目のモーションを表示させているのか、というのを表す変数を用意して、それを描画毎に変化させていけばいいのですが、それは宿題としましょう(笑)
基本的に、敵キャラも、表示・移動だけでいいのですが、移動に関してはユーザーが入力してくれるわけではないので自動的に動く必要があります。場合によっては、何らかの知能を持たせなくてはなりません。単純には主人公をめがけてまっしぐらでも構いませんし、岩とかならば下に落ちてゆくだけでも構いません。
今回は、あまりそのへんを突っ込んで解説すると難しくなりすぎるので、岩を落としてみましょう。このビットマップを使いましょう。(クリックしてファイルに保存し、その後ygs2000.exeと同じフォルダに移動させてください)
void main(){ int n,x,y; int x2,y2; // 岩の座標を保持するための変数宣言 LoadBitmap("ash.bmp",0,1); LoadBitmap("iwa.bmp",1,1); // 岩のビットマップをプレーン1に読み込む SetFPS(10); n = 0; x = 100; y = 100; x2 = 200; y2 = -16; // 岩の座標として(200,-16)を初期位置とする loop { if (n!=0) n = n + 1; if (n==5) n = 0; KeyInput(); if (IsPushSpaceKey()) n = 1; if (IsPressUpKey()) y = y - 8; if (IsPressDownKey()) y = y + 8; if (IsPressLeftKey()) x = x - 8; if (IsPressRightKey()) x = x + 8; y2 = y2 + 16; // y2に16加算する if (y2>480) y2 = -16; // y2が480を越えていれば-16に戻す ClearSecondary(); BltRect(0,x,y,n*120,0,120,82); Blt(1,x2,y2); // プレーン1を(x2,y2)に描画する halt; } } |
新しい命令は何も出てきてませんので、難しくはないと思います。実行すると、以下の画面のように上から岩が一つ降ってきて、それが消えると再度同じ場所から降ってきて...というように動きます。
5.接触判定を入れる
敵キャラの表示、そして移動も、なんとなくわかっていただけたと思います。実際、この敵キャラの移動の部分に関してはプロでもいろいろ悩む部分であり、本来はそういうのを考えるのはプログラマーの仕事ではなくゲームプランナーの仕事なのかも知れません。(だからこそ、そのへんにプロのノウハウや苦労が見え隠れするのですが)
ともかく、岩と主人公の接触判定をどうするか考えましょう。シューティングならば敵の弾に自機が接触したとか、自分の発した弾に敵が当たっただとか、キャラクターゲームならば敵に自分が捕まっただとか、岩を剣でくだいただとか、さまざまな場面で必要になります。基本的には、主人公と敵は両方矩形と見なして、矩形対矩形で交差するかを調べるのが一般的でしょう。(シューティングでは、自機を点と見なして、敵を矩形と見なして、点対矩形で包含されるかを調べるものも多いですが)
具体的に、図示して説明します。
if ( (x1 == x2) & (y1 == y2) ) { //当たり! } |
if ( ((x1 < ten_x) & (ten_x < x2)) & ((y1 < ten_y) & (ten_y < y2)) ) { //当たり! } |
if( (x1 < x4) & (x3 < x2) & (y1 < y4) & (y3 < y2) ) { //当たり! } |
今回は、あまりそのへんを突っ込んで解説すると難しくなりすぎるので、岩を落としてみましょう。このビットマップを使いましょう。(クリックしてファイルに保存し、その後ygs2000.exeと同じフォルダに移動させてください)
void main(){ int n,x,y; int x2,y2; LoadBitmap("ash.bmp",0,1); LoadBitmap("iwa.bmp",1,1); SetFPS(10); n = 0; x = 100; y = 100; x2 = 200; y2 = -16; loop { if (n!=0) n = n + 1; if (n==5) n = 0; KeyInput(); if (IsPushSpaceKey()) n = 1; if (IsPressUpKey()) y = y - 8; if (IsPressDownKey()) y = y + 8; if (IsPressLeftKey()) x = x - 8; if (IsPressRightKey()) x = x + 8; y2 = y2 + 16; if (y2>480) y2 = -16; if ((n!=0) && (x<x2+32) && (x2<x+32) && (y<y2+32) && (y2<y+32)) y2 = -16; ClearSecondary(); BltRect(0,x,y,n*120,0,120,82); Blt(1,x2,y2); // プレーン1を(x2,y2)に描画する halt; } } |
赤字の部分が追加分です。剣を振っていて、かつ矩形対矩形で交差していれば、岩のY座標を-16にするという処理を入れました。実際に動かして、岩の前で剣を振ってみてください。
6.最後に
今回は、マイキャラのアニメーション表示・移動、そして敵キャラの移動、接触判定を勉強しました。どんなゲームでも必要になりそうな、根幹部分なので、いろいろ自分なりに試してみることをお勧めします。
また、ygs2kはユーザー同士の交流も盛んなので、是非インターネットに接続してご覧になってください。そして、面白い作品が出来たなら、是非、私に教えてくださいな。
実は、今回、『Revolution』の改造記事書こうと思ってたんですが、まだゲーム本体がそこまで出来てないのです(笑)
前回、『夜這いマニア』のときも改造記事を先に書いてしまって、結局、それに合わせて作らざるを得なくなってえらく困ったので(笑)
そこで、今回は、これをプレゼント。これは、ygs2kで開発したファイルを一まとめにするためのものなのですが、少なくともWAFFLE制作・販売の『盗撮マニア』,『Message』,『夜這いマニア』で使用しています。つまり、これさえあれば、全てのCGが見れるばかりでなく……何もかもが……と言ってもモザイクが取れるわけではないので、そういう期待はしないように(笑)
えっと。次回は、ygs2kで、ある程度遊べるゲームを作ることにしましょう。
それから年末発売予定の双子を題材にした恋愛ゲーム『Twin
Love』の紹介が...できるといいですね(笑)
それではまた次号でお会いしましょう。