Visual Basic ゲーム講座 |
この記事が対象とする製品・バージョン (バージョンの確認方法)
Visual Basic 2010 | ◎ | 対象です。 | |
Visual Basic 2008 | ○ | 対象ですが一部画面が異なる場合があります。 | |
Visual Basic 2005 | ○ | 対象ですが一部画面が異なる場合があります。 | |
Visual Basic.NET 2003 | × | 対象外です。 | |
Visual Basic.NET (2002) | × | 対象外です。 | |
Visual Basic 6.0 | × | 対象外です。 |
ゲーム講座では基本的なゲームの作り方を解説します。題材として単純なシューティングゲームを取り上げますが、手法としては多くのゲームで共通すると思いますのでゲームを作りたいけどどうやって作ればよいかわからないという人には参考になると思います。
数回に分けて1つのゲームを完成させるところまでもっていきたいと思っていますが、最近なかなか約束通りに記事が書けないので、今回ははじめから約束しないことにします。つまり、この続きは永遠に執筆されない可能性があります。
読者の レベルは初級講座を修了相当が望ましいですがチャレンジ精神がある方であれば修了前から読んでみてもいいかもしれません。入門講座修了相当は必須です。
概要 ・画像はbmp形式で用意する。 ・bmpファイルはプロジェクトに取り込み、「新しい場合はコピーする」を選択する。 ・Timerをつかって繰り返し再描画する。描画処理はTimer1_Tickイベントでのみ行う。 ・キーボードからの入力はKeyDownイベントとKeyUpイベントで処理する。 |
キャラクターを表示させるにはまず画像を用意します。単純な丸や四角の形のキャラクターであれば画像を用意しないでVBのグラフィックス機能で描画することもできますがそれでも画像を用意するのが定石です。
画像はbmp形式がよく、保存時に形式を選べるのであれば24ビットのbmpにしてください。bmp形式の画像ファイルはサイズは大きくなりますが最も処理速度が速い形式です(たまにファイルサイズが小さい方が処理速度が速いと勘違いして一生懸命jpegで作成してプログラムの処理速度が遅くなってしまう人がいます)。
プログラムの初期段階では仮の画像を用意しておいてください。最初から画像に力が入るとプログラムがなかなか進みませんし、後でプログラムの都合で画像の変更が必要になった時にかなりモチベーションが下がります。ですので、まずはWindowsに必ず付属しているペイントで適当にそれらしい絵を描けばよいでしょう。
画像のサイズはゲームによって異なると思いますので自由でよいですが、大きすぎる画像はそれなりに処理に時間がかかります。今回はは64x64の画像にします。
また、画像はかならず四角であるのに、ゲームのキャラクターのほとんどは四角ではないので、背景を透明にする処理が必要になります。
VBでは特定の一色を透明にする機能があるので、画像を用意する段階でどの色を透明にするか決めておいてください。この特定の一色は画像ごとに変更することもできますがプログラムの統一性を考えるとすべての画像で共通した1色にしておくのが良いです。黒や白、赤、青などのよく使う色を透明にすると画像の中で黒、白、赤、青などが使えないことになってしまって面倒です。黒や白、赤、青に非常に近い色をつかうことで回避もできますが面倒なので止めましょう。よくつかわれる透明色はこのどぎつい紫色です。
■画像1:大分画質が悪くなってしまいましが、このRGB(255, 0, 255)の色がよく透明色に使われます。
とりあえず次の画像を主人公としてみます。
■画像2:主人公
ファイル名はPlayer1.bmpにします。本来ファイル名はなんでもよいのですが、この説明に合わせて作業するのが楽になるようにPlayer1.bmpという名前にすることをお勧めします。名前から想像がつくと思いますが、後でPlayer2.bmpという名前の画像も登場する予定です。
この画像をダウンロードして使用しようと思っている人は画像の形式をbmpに変換するのを忘れないでください。
|
画像の準備ができたらVBのプロジェクトを新規作成しましょう。ここではVB2010 Expressを使ってVBGameD01という名前のWindows フォーム アプリケーションを作成します。
最初ソリューションエクスプローラーには次のように表示されています。
■画像3:ソリューションエクスプローラーの初期状態
ここで、プロジェクトであるVBGameD01を右クリックして[追加] - [既存の項目の追加]を選択し、今作成した画像ファイルを選択してください。このときはじめフィルターが画像ファイルを表示しない状態になっているので自分ですべてのファイルを表示するようにフィルターを変更してからPlayer1.bmpを選択します。
■画像4:初期状態のファイル選択ダイアログ
■画像5:フィルターを変更すると画像も選択できるようになります
ファイルを選択するとソリューションエクスプローラーにファイルが追加されます。
■画像6:画像追加後のソリューションエクスプローラー
この段階でいったんプロジェクトを保存してください。そのあと何も表示されませんが一度実行してみてください。すぐに実行を終了させ、プロジェクトを保存したフォルダーの中にあるbin\debugフォルダーをみてください。プログラムを実行するとこのbin\debugフォルダーの中にexeが作成され、それが実行されます。今、実行したのでみなさんのbin\debugフォルダーにもexeファイルが作られているはずです。
■画像7:bin\debugフォルダーの中
この中にはPlayer1.bmpがありません。この状態だといくらプログラムを追加しても画像ファイルのパスは固定にせざるを得なくなりとても使いにくいゲームになってしまいます。たとえば、「ゲームをするには必ずC:\ImageフォルダーにPlayer1.bmpを入れておいてください。」などということを言わなくてはいけなくなりとても不便でかっこ悪いです。
こうしなくて済むようにソリューションエクスプローラーでPlayer1.bmpをクリックして、プロパティウィンドウを表示させ、「出力ディレクトリにコピー」の欄を「新しい場合はコピーする」にしてください。
■画像8:出力ディレクトリにコピー
これで実行すると、今度はbin\debugフォルダーにPlayer1.bmpがコピーされるのがわかります。
■画像9:bin\debugフォルダーの中
以上で準備は整いました。
では、この主人公をさっそく表示してみましょう。VBで画像を表示する方法はいくつかありますが、あとでこのキャラクターが動き回ったり、弾をうったりすることを考えてゲームに適した表示方法を採用します。
ゲームに適した表示方法とは短い時間で何度も画面を再描画する表示方法です。
まだキャラクターが移動しないので一度描画してしまえば終わりですが、移動するようになると何度も何度も描画することになります。
短い時間に何度も画面を再描画するにはループを使うかタイマーを使います。PlayStationやNintendo DSの場合はタイマーがないのですべてループで描画しているはずですが、VBの場合、便利なタイマーが使用できるのでタイマーを使用するのが良いです。
そこで、要するにどうすればいいかというと、まずはフォームにTimerを1つ貼り付けてください(なお、Timerはフォームに貼り付けるとフォームの下の部分に表示されます)。Timerの設定は特に変更しないで良いです。初期状態ではTimerは1秒間に10回のTickイベントを発生させます。
なお、Timerはツールボックスの「コモン コントロール」ではなく、「コンポーネント」の中にあります。または、「すべてのWindows フォーム」から探してもよいです。
Timerの設定が済んだら、フォームに次の通りプログラムします。
Private mainGraphics
As Graphics Private PlayerImage As Bitmap |
Private Sub Form1_Load(ByVal sender
As System.Object,
ByVal e As
System.EventArgs)
Handles MyBase.Load Init() Timer1.Enabled = True End Sub |
Private Sub Init() '▼描画用のGraphicsクラスの確保(技術的な処理) If mainGraphics Is Nothing Then '初回のみ生成 Dim bmp As New Bitmap(Me.ClientRectangle.Width, Me.ClientRectangle.Height) Me.BackgroundImage = bmp mainGraphics = Graphics.FromImage(bmp) End If 'exeと同じフォルダーにあるPlayer1.bmpを読み込む。 'これができるのは、ファイルのプロパティで出力ディレクトリにコピーする設定になっているから。 PlayerImage = Image.FromFile(Application.StartupPath & "\Player1.bmp") End Sub |
Private Sub Timer1_Tick(ByVal sender
As System.Object,
ByVal e As
System.EventArgs)
Handles Timer1.Tick mainGraphics.DrawImage(PlayerImage, 0, 0) 'フォームを再描画 Me.Invalidate() End Sub |
■リスト1
このコードで「▼描画用のGraphicsクラスの確保」とコメントしてある部分は、経験がない人にはちょっとわかりにくいかもしれませんが、何をやっているかと言うと、VBでは実際に描画を行うときはGraphicsクラスに命令して描画を行うのですが、ゲームのように短い間隔で何度も何度も再描画する場合そのたびにGraphicsクラスを生成しているとコストが悪いこと(コストが悪い = 実行に時間がかかる)、それとフォームからGraphicsクラスを生成するとGraphicsクラスの生成が.NET Frameworkまかせになってしまいこちらで制御できないことから、自分でフォームと同じ大きさの画像を作り、その画像をフォームの背景画像に指定し、その画像のGraphicsクラスを先に生成しておくということをやっています。
こうしておくと、このGraphicsクラス、変数でいうとmainGraphicsに描画を命令してFormのInvalidateメソッドを呼び出すことでこちらで描画した内容をいつでもフォームに転送(というか表示)できるようになります。
つまり、今後フォームに描画したい場合はmainGraphicsのメソッドを使って描画する、描画後FormのInvalidateメソッドを呼び出すという流れになります。
それがまさにTimer1_Tickで行っていることです。
これで地味ですが、1秒間に10回主人公の絵が描画されます。
■画像10:実行画面
1回の描画処理のことを「フレーム」と呼びます。この言葉は重要なので覚えておいてください。
つまり、このプログラムでは1秒間に10回描画が行われるので、1秒間に10フレームというわけです。1秒あたりのフレーム数をフレームレート(fps)と呼びますが、この言葉はこの講座では使わないようにするのでここでは覚えなくてよいです。「フレーム」は頻繁に使うので必ず覚えてください。
しかし、毎回この説明をするのも邪魔だと思うので、今後は単純に「1秒あたり10フレーム」などと言い切ってしまうこともあります。 TimerがTickイベントを発生させる時間間隔の努力目標はIntervalプロパティで設定できます。 |
現段階では 画像が動かないので1秒間に何フレームであるかはどうでもよく、そもそも何度も再描画する意味はありません。
しかし、何度も再描画しているために、実行してしばらく画像を見ていると画像がちらつくのがわかります。このちらつきはゲームプログラマーの敵です。これがあるといくらこったプログラムを作っても画面がちらちらちらちらして一向にゲームに身が入りません。
このちらつきをなくすにはダブルバッファリングという手法を使用します。幸いVBではプロパティをTrueにするだけで簡単にダブルバッファリングを使用できます。その名もFormのDoubleBuffedプロパティです。このプロパティをTrueにして再実行してみてください。ちらつきがなくなっています。
次にこのキャラクターを移動させてみましょう。本来はプレイヤーの操作で移動するのですが、この段階では自動的に移動させるようにプログラムしてみます。
さて、「移動」とは何かを考えてみると、実はパソコンやTVの画面に表示されているキャラクターや芸能人の移動とは、本来の移動ではなくちょっとずつ表示する場所が変わっているだけということがわかります。そもそもパソコンのディスプレイはドット単位で描画しており、ドットは固定されていて移動などできるような作りになっていません。TVも同様です。
なので、これから「移動」というとき、それは本当の「移動」ではなく、人の錯覚を利用した疑似的な「移動」であるということを忘れないでください。→興味がある人は「ファイ現象」、「ベータ運動」を調べてみてください。駅の電光掲示板でメッセージが流れていくように見えるのはベータ運動と呼ばれ、移動の錯覚です。
前置きが長くなってしまいました。要するに「移動」させるには、ちょっとずつ座標を変えて、絵が移動しているように見ている人に錯覚させればいいわけです。
上のプログラムで表示する座標はGraphicsクラスのDrawImageメソッドの第2引数と第3引数で指定しています。
mainGraphics.DrawImage(PlayerImage, 0, 0) |
ここが0, 0になっているので、とりあえず座標(0, 0)、つまり一番左上に画像が表示されているわけです。
ちょっとずつ値を変えたいので変数にしてみましょう。Timer1_Tickイベント中を次のように改造してください。
Dim x As Integer Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick mainGraphics.DrawImage(PlayerImage, x, 0) x += 1 'フォームを再描画 Me.Invalidate() End Sub |
■リスト2
これでちょっとずつ(1ピクセルずつ)主人公が右に移動していくことになります。
■画像11:1ピクセルずつ右に移動
念のために書いておきますが、Dim x As IntegerをSubの中に書いてはダメです。Subの中に書くとTimer1_Tickが実行されるたびにxが生成されるので、毎回xの値は0になりちっとも移動しません。
…にしても、残像が残ってしまいますね。これがDrawImageで描画ばかり命令していて描画したものを消すという命令をしていないせいです。消す命令も追加してみましょう。描画内容を消すにはClearメソッドを使用します。
Dim x As Integer Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick '描画されているものを消して全面を黒くする。 mainGraphics.Clear(Color.Black) mainGraphics.DrawImage(PlayerImage, x, 0) x += 1 'フォームを再描画 Me.Invalidate() End Sub |
■リスト3
これで実行するとそれなりに移動します。
■画像12:クリア処理を追加
それにしても、移動の速度がゆっくりですね。移動速度を速くするには1フレームあたりの移動量を多くするか、Timer1のTickイベントが実行される頻度を上げて1秒あたりのフレーム数を増やします。移動量を多くするには x += 1 の部分を x+= 2 や x+= 3にします。タイマーが実行される頻度を上げるにはTimer1のIntervalプロパティの値を小さくします。
今回はTimer1のIntervalプロパティを10にしてみてください。
これで実行すると1秒あたりのフレーム数が100になり、さきほどの10倍の速さで移動します。速度の調節はゲームができてきてから考えるとして今はこれでOKとしておきましょう。
この時間の間隔を示しているのがIntervalプロパティです。Intervalプロパティは1000で1秒という意味で、初期値は100で、0.1秒を表します。このように1秒を1000であらわす単位を「ミリ秒」と呼びます。 TimerはIntervalの設定ごとにTickイベントを発生させるように努力してくれますが、この設定はあくまでも努力目標であって細かい値を指定した場合やTickイベント内で時間のかかる処理をしている場合などは実際にその通りの間隔でTickイベントが発生しないことも珍しくありません。(ただし、間隔が設定値より短くなることがないことは保障されています。) |
今度はプレイヤーの操作によってキャラクターが右に行ったり左にいったりするようにしましょう。
プレイヤーがどのように操作するかはプログラマーが自由に決めることができます。今回はよくあるようにキーボードの右矢印を押したら右に、左矢印を押したら左に移動するようにします。
このように決めると、多くの人はフォームのKeyPressイベントにプログラムを書こうとしますが、KeyPressイベントはアプリケーションのための高度なイベントであって、ゲームの操作には向きません。ゲームではキーボードからの操作を受ける最適なイベントはKeyDownイベントとKeyUpイベントです。
そして、とても重要なのことですが、描画はすべてTimer1_Tickイベントの中で行うので、KeyDownイベントやKeyUpイベントでは描画は一切しないでください。KeyDownイベントやKeyUpイベントで一生懸命描画しても1秒間に100回発生するTimer1_Tickでクリアして上書きされてしまい無意味ですし、描画を行う個所は一か所と決めておいた方がゲームが複雑になってきてもプログラムはあまり複雑にならないで済みます。
どうすればいいかというと、KeyDownイベントでは矢印キーがおされたというフラグだけ立てておきます。「フラグを立てる」というのはプログラムでよく使う言葉ですが「変数に記録しておく」というくらいの意味です。
KeyUpイベントでは矢印キーが離されたときにフラグをおろします。つまり記録しておいた変数から記録を削除します。
立てたりおろしたりするフラグに向いている型はTrueかFalseかを表現するBoolean型です。
KeyDownとKeyUpのプログラムは次のようになります。
''' <summary>右矢印が押されているか</summary> Dim IsRight As Boolean ''' <summary>左矢印が押されているか</summary> Dim IsLeft As Boolean Private Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown Select Case e.KeyCode Case Keys.Right IsRight = True Case Keys.Left IsLeft = True End Select End Sub |
Private Sub Form1_KeyUp(ByVal sender
As System.Object,
ByVal e As
System.Windows.Forms.KeyEventArgs)
Handles MyBase.KeyUp Select Case e.KeyCode Case Keys.Right IsRight = False Case Keys.Left IsLeft = False End Select End Sub |
■リスト4
このプログラムを見るとわかるようにKeyDown、KeyLeftでは変数をTrueにしたりFalseにしたりしているだけです。
実際にこの情報に基づいて描画を行うためにTimer1_Tickイベントを次のように修正します。
Dim x As Integer Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick '描画されているものを消して全面を黒くする。 mainGraphics.Clear(Color.Black) If IsRight Then x += 1 End If If IsLeft Then x -= 1 End If mainGraphics.DrawImage(PlayerImage, x, 0) 'フォームを再描画 Me.Invalidate() End Sub |
■リスト5
先ほどは自動的にxに1をたしていたのを止めて、右矢印が押されていればxに1をたす、左矢印が押されていればxから1をひくというわけです。両方押されてる場合は1をたしてから1をひくのでなにも変化しません。
これであなたのキーボード操作に従ってキャラクターが動くようになります。ここまでできてくるとなんかわくわくしてきます。
書いてる通りに操作して、コードもコピー&貼り付けしてとりあえず動くようになったけど、結局よくわからないという人はいませんか?
そんな人は今度は縦方向にも移動できるようにプログラムを改造してみてください。
この段階で、縦方向の移動が自分でプログラムできなければ次の段階に進んでもよくわからないままになってしまいます。頑張って四苦八苦しましょう。これが自分でできないようだとこの先ゲームを作っていくのは相当厳しいです。
横と縦が変わっただけでプログラム的には新しい要素はないはずです。
それができたら、下矢印と右矢印を同時に押すと右下に移動するようにもしてみてください。
一応完成版をダウンロードできるようにはしておきます。できるだけ見ないでくださいね。
ダウンロード VBGameD01_1.zip 13KB
次回は敵キャラクターを出現させます。
今後の予定。冒頭にも書いたように続きを書くかどうかはまったくわかりません。
第2回 敵
第3回 キャラクターのクラス化
第4回 あたり判定・敵との戦い
第5回 弾
第6回 音楽と効果音
第7回 いろいろな敵
第8回 運動