ExcelVBAゲームプログラミング?

初心者でもきっとできる!
Excelさえ持っていれば特別なソフトは不要!
すぐにでも始められる簡単ゲームプログラミング!
今すぐ始めよう!

サンプルやゲームのダウンロードができる別館も好評運営中です。
ご意見やご質問、ゲームの感想等は掲示板までお気軽に。是非、皆さんの声を聞かせてください。運営、開発の励みになります。



各種ダウンロードはコチラ ↓ 意見・感想・質問はコチラ ↓
影倉庫 Shadow warehouse サポート掲示板
ブログの全体像はコチラ ↓ リンクのページはコチラ ↓
サイトマップ 自分本位なリンク


スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。






なにが起こった?

な、なにがおこったんだ……。

昨日から急激なアクセス数の伸び……。これはどこかでDEX-EVが紹介かなんかされたってことなんだろうか。
とにかく、アップローダは当然期限切れでIDが無効になってるはず。いらっしゃった方々には、ご迷惑をお掛けしたことでしょう。すいません。

現在は、われらがExcelプログラマーの牙城、近田さんのサイトからダウンロードできます。が、近田さんのご好意により、ファイルへの直リンクをはりました。近田さん、感謝です。


こちらよりダウンロードできます。⇒ DEX-EVダウンロード

年末年始、パソコンに触れなかったので、状況を把握してませんでした。ごめんなさい。
スポンサーサイト






Chapter.29 [ フォーム上の位置情報 ]

■宝の地図

ある日、彼は宝の地図とおぼしき、汚れた布切れを見つけた。
最初は、自分でもまさか……と思ったのだが、よくよく見てみると、いかにも本物っぽい気がしてきた。ぼんやりとではあるが、地図のように見える図形がいくつかあり、端々には、文字のようなものも書かれているようだ。
ほとんどの文字は、かすれていたり、ちぎれて途中で切れてしまっていた。だが、たった一文、短い文章ではあったが、なんとか読み取れそうなものを見つけた。そして、そこにはこうあった。
『上から5、右から10』……思わず呟いた。『どこを始点にしてんだよ……これ』

はい。全くプログラミングと関係のない小話です。でも、今回の講座には、これとよく似た考え方が必要な場面があるのです。

今回は、プログラミングする際に必要となる、表示位置に関する概念を解説します。
表示位置、とひとくちに言っても、中々想像がつかないかもしれませんが、プログラミングする際には欠かせない概念のひとつです。
特に、ゲームのプログラミングに関して言えば、これがキチンとわかっていないと、キャラクターひとつ表示することができません。まぁ、表示はできたとしても、それを滑らかに動かしたりすることはできないでしょう。

今回の概念をしっかり覚えて、基礎を固めておきましょう。


■表示位置の概念

さて、以前の講座で、ユーザーフォーム上に画像を表示させる方法を解説したのですが、覚えているでしょうか。
具体的には、イメージコントロールを配置して、そのコントロールのPictureプロパティに任意の画像をロードさせればよかったんでしたね。
その後、様々なプロパティを変更・設定することによって、背景の透けた画像を用意する方法なども解説しました。いずれにしても、キチンと順を追って進めてきた方なら、ここまでは問題ないはずです。

さて、今回は、このキャラクターを表示したコントロールなどの、様々なモノ(オブジェクト)が、どのような概念で、画面上に表示されているのか考えてみます。具体的には、その表示される位置は、どのようにして決められているのか? ということですね。

VBAに限ったことではないのですが、基本的に位置情報というのは2つの要素から成り立っています。その2つの要素とは、ズバリ『横位置』と、『縦位置』の2つです。

・横位置……左端からどのくらいの距離にあるか
・縦位置……上端からどのくらいの距離にあるか


表示したいモノが、ある地点から見て、右の方にいるのか、それとも左の方にいるのか。そして同様に、上の方にいるのか下の方にいるのか。この横位置と縦位置の、2つの位置情報があれば、平面上での正確な位置を表すことができるのですね。

そして、上の枠の中でも太字で表示されていますが、プログラミングの時に最も大切なのは、『左端から~』と『上端から~』という概念です。下から、とか、右から、ではないので間違えないようにしましょう。

もしも、横位置の値が『0』だったときには、それは左端を表します。同様に、縦位置の値が『0』だった場合には、それは上の端を表します。これは図解すると非常にわかりやすいですね。フォームのはじっこのところです。

150.gif


そして、横位置の値をプラスしていくと右方向へ。マイナスしていくと左方向へ、表す位置が変動していきます。
同様に、縦位置の場合には、値をプラスするほど下方向へ、マイナスするほど上方向へ、位置が変動します。

この、『横位置の値』と『縦位置の値』を組み合わせることで、ユーザーフォーム上の全ての位置を表すことができるのがわかると思います。

そして、この横位置と縦位置を定義するプロパティが、『Left・Top』の2つのプロパティです。
Leftプロパティが横位置を、Topプロパティが縦位置を表します。この2つのプロパティを様々に変化させることで、ユーザーフォーム上に表示される位置を、任意の場所に変更することができるのです。

151.gif


理屈がわかれば、それほど難しくないですよね。


■指定する数値について

さて、ユーザーフォーム上の位置を表すために、縦横2つの情報が必要なのはわかりましたね。そして、それを指定するプロパティとしてLeftプロパティとTopプロパティがあるということも、理解できたと思います。

しかしここでひとつ疑問がでてきます。

ユーザーフォーム自体が、横幅100ポイント分の大きさしかないとします。そんな時、それよりも大きな数値の横位置を指定したら、一体どうなるのでしょうか。

実際にやってみればよくわかると思いますが、ユーザーフォーム上に配置したコントロールのLeftプロパティに、100よりも大きな数値を指定してみると、ユーザーフォーム上から見えなくなってしまいます。
これはTopプロパティについても同様です。ユーザーフォームの縦の長さよりも大きな数値を指定すると、やはり、ユーザーフォーム上から見えなくなります。

注意しなければならないのは、ユーザーフォーム上から、対象となるコントロールが『なくなってしまったわけではない』ということです。
あくまでも、横位置や、縦位置が見える範囲の外に設定されているだけであって、コントロール自体がなくなってしまったわけではありません。単純に見えていないだけのことなのです。

その証拠に、プロパティウィンドウにあるドロップダウンリストには、キチンとコントロールが含まれているはずです。

152.gif

153.gif

154.gif


プロパティウィンドウには、こういったコントロールに関する情報が含まれているのですね。このことを知っていると、マウス操作のミスなどでコントロールがどこかへ行ってしまったときや、透明になっていて見えないコントロールを探すときなど、意外と便利ですので覚えておいて損はないでしょう。


■負の世界

LeftプロパティやTopプロパティには、負の数値を指定することも可能です。マイナスの数値っていうことですね。

2つのプロパティに大きすぎる数値を指定したときと同様に、小さすぎる(マイナスの)数値を指定したときも、やはりユーザーフォーム上から見えなくなります。
ユーザーフォームの左の端が0ポイントですから、それよりも小さい数値をLeftプロパティに設定すると、左の方向へどんどんめり込んでいって、最終的には見えなくなります。

Topプロパティなら、ユーザーフォームの上の端が0ポイントですから、それよりも小さい数を指定すると、どんどん上にめり込んでいって、マイナス幅が大きくなればいずれ見えなくなります。

例えば縦にスクロールするシューティングゲームなどでは、敵のキャラクターが出現する際にこの仕組みを活用できますね。
登場するときにはあらかじめマイナスの数値を設定しておいて、徐々に数値をプラスしていけば、画面の外から現れる敵キャラクターを演出できます。突然パッと画面に表示されるよりも、自然な演出だと言えるでしょう。

普通の業務アプリケーションなどでは、LeftTopにマイナスの数値を指定することはまずないでしょうが、ゲームのプログラミングということになると、こういったテクニックも必要です。
いずれ具体的に解説するとして、今回はそういったやり方もあるということを覚えておきましょう。


さて、今回は表示位置の仕組みについて見てきました。どうでしょうか、理解できたでしょうか。
わかってしまえばどうということはないのですが、初めてのときはどうしても戸惑います。私も最初はちょっとわかりづらかったです。普段の生活ではこのように位置を指定することってないですからね。

ゲームの作成には欠かせない表示位置の概念。しっかり基礎を固めておきましょう。


■格言

Leftプロパティで横位置(左から)
Topプロパティで縦位置(上から)
2つのプロパティにはマイナスの数値を設定することも可能


好きな場所に表示できるようにしておきましょう。






Chapter.30 [ API基礎知識 ]

■出た!難敵API

全然ゲームのプログラミングに関する解説が出てきませんね。毎回更新するたびに見てくださっている方がいるなら、ちょっと物足りないかもしれません。ゲームを作りたくて、足を運んでくださっているなら、尚更申し訳ない。

しかしながら、今回の内容までを説明しないと、具体的な話に移れないんです。ご了承くださいね。

さて、今回は掲題の通り、通称API(エーピーアイ)と呼ばれている仕組みについて解説します。この仕組みを理解していることは、非常に強力な意味を持ちます。同時に、非常に難解な部分でもあります。
その難解さ故に、なかなか理解できずに苦しんでいる方も、いるかもしれません。
私の場合は、全くの初心者から、独学のみでプログラミングを習得してきましたので、最初の頃はハッキリ言って、ほとんどAPIなんて意味不明な世界でした。
できる限り、わかりやすく、必要な部分だけ解説したいと思います。ですから、諦めず、また、めげずに習得していただきたいと思います。


APIは、『Application Programming Interface』の頭文字を取った略称です。一体なんのこっちゃって感じですね。
簡単に要約すると、ウィンドウズに始めから搭載されており、プログラミングを行う際に役にたつ機能の集合体、っていう感じでしょうかね。

実は、皆さんが利用している、言わずと知れたウィンドウズ、その機能のほとんどは、このAPIが制御しています。ウィンドウズ自体が、APIで設計されているのです。
当たり前のように使っているウィンドウズの機能は、APIが働いてくれているおかげで成り立っているのです。
ファイルを保存したり、消したりコピーしたり。あるいは音楽や効果音の再生。時間の管理や、メモリの管理。ウィンドウの表示や非表示、拡大縮小などなど……。とにかく挙げればキリがありませんが、ウィンドウズのありとあらゆる機能の根底には、APIがあるのです。

そして、VBAからも、このAPIを使うことができるのです。なんと素晴らしい。
どうしてそれが素晴らしいのか、ちょっとそれを考えてみましょう。


■APIの必要性

ゲームの演出には、様々な視覚的効果はもちろんのこと、音に関する演出も重要です。かっこいいBGMを鳴らしたり、派手な効果音で場面を盛り上げたり、音を扱うシーンは多くあります。

しかし、VBAに標準で搭載されている機能の中に、音に関する機能というのはほとんど無いと言っていいでしょう。一応『Beep』とコードを記述すると、ビープ音を鳴らすことができるんですが、これではとてもゲームの演出には活用できません。プーとかピッとかだけじゃつまらないですもんね。

こんなとき、APIをうまく利用することで、様々な音楽や効果音を鳴らすことができるようになります。拡張子が『wav』となっている、いわゆる『Waveファイル』の再生程度ならAPIを利用して比較的簡単にできます。
少し難しいですが、キチンとコーディングすれば『MIDIファイルとWaveファイルの多重再生』なんてことも可能です。


それから時間の管理についても、APIの力を借りることでいろいろなことができるようになります。
VBAの基本機能だけだと、最も短い時間の単位が『1秒単位』になります。これは考えてみるとすごく遅いんですね。特にゲームに於いてはこれは致命的です。

例えば皆さんが今ご覧になっている、パソコンの画面で考えてみましょう。
これは設定にもよりますが、大体1秒間に60回更新されています。なかなか実感がわきませんが、い~ち、と数える間に60回程度、画面が描きなおされているのです。このおかげで、動画やアニメーションが滑らかに、スムーズに表示されるのですね。
Word(ワード)などで文字を入力していても、画面が1秒ごとにしか更新されなかったら、ものすごいストレスになるでしょうね。

VBAが自力で出来るのは、1秒単位が最速なので、これではゲームに適していません。
しかしAPIを使うことによって、この単位を最高でミリ秒単位まで速くできます。
1ミリ秒というのは『1000分の1秒』ですから、ポテンシャルは1000倍近いということになります。なんと凄まじいことでしょう。これなら、ゲームをガリガリ動かすことができます。

このように、APIを活用することによって、VBAだけでは実現不可能な、高度な処理が可能になるのです。


■じゃあ具体的にはどうするの?

APIがゲームの作成に必要だということは、なんとなく理解できたでしょうか。

VBAでゲームをプログラミングする際に、まず必要なのは時間の管理、これが第一だと私は考えています。先にも書いたように、時間の管理に関してはAPIの力を借りざるを得ません。
そこで今回は、APIの基本的な記述方法と共に、『Sleep関数』についてみていきたいと思います。

Sleep関数は、任意の時間、処理をストップしたいときなどによく使われます。その最小単位は『ミリ秒単位』です。つまり、このSleep関数をうまく利用すると、1秒よりも細かい単位で時間を制御することができます。

それでは具体的な使い方を見てみましょう。

APIを使うためには、事前に準備が必要です。ここがちょっと難解な部分なんです。
ですが心配は無用。やるべきことは『コピー』と『貼り付け』だけです。非常に簡単です。

まずは以下のコードを見てください。

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)


なにこれ……、って感じだと思いますが、これがAPIを使うために必要な準備の全容です。
上のコードを、モジュールの先頭部分にコピーして貼り付けすることが、APIを使うための準備です。簡単ですね。

以前、変数の話をしたときに、Publicで宣言するときは、モジュールの先頭で変数を宣言する、という話をしましたが、覚えているでしょうか。
これと同様に、上記のコードを、モジュールの先頭部分にコピーして貼り付けておきましょう。
すると、たったこれだけのことで、Sleep関数というAPIを使うことができるようになります。

そして、Sleep関数を使ってみたコードが以下のような感じ。

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)

Sub Sleep_Test()
    Call Sleep(3000)
    MsgBox "3秒間処理を止めました"
End Sub


なんとまぁ、簡素なコードですね。自分で言うのもなんですが。

とにかく、上のコードを実行するとどうなるのか、実際にやってみてください。
内容としては、実行開始から、3秒間だけ処理をストップさせて、そのあとにメッセージを表示しているだけです。
注目すべきは、時間の指定方法です。
CallをつかってSleep関数を呼び出していますが、そのあとに続く括弧の中には、『3000』と記述されていますね。
Sleep関数は、先にも述べたとおり、『ミリ秒単位』で時間を制御します。ですから、1秒は『1000』、5秒なら『5000』、10秒を指定したいなら『10000』という具合に指定する必要があります。もちろん、これは1000の倍数である必要はありません。極端な話『Call Sleep(1)』でも問題ありません。
括弧の中に処理をストップしたい分だけ、任意の時間を指定すればいいだけです。いろいろ変えて実行してみれば、その違いがわかるでしょうから、試してみるといいでしょう。


■ちょっと難しい話だけど重要なこと

今回は、APIについて、初めて使う際に必要なことだけ、簡潔に説明してきました。
ちょっと不可解な部分は残っているでしょうが、使うだけなら意外と簡単だということも、実感できたのではないでしょうか。

ただ、始めにこれだけは理解しておいてほしい、ということがあります。

APIは、VBAだけでプログラミングすることに比べて、非常に高度な知識を必要とします。しかしその代償として、非常に強力な機能を扱うことが可能となり、今までは不可能だと思っていたことが、案外簡単に実現できたりします。
しかし、絶対に忘れてはならないのは、『APIは使わないに越したことはない』ということです。実はこれがとても重要です。

ある特定の目的があって、どうしてもAPIを利用したい場合を除いて、できる限りAPIなしでプログラミングできることが一番理想的です。
なぜなら、APIのみで処理を実行するだけでいいのなら、VBAの存在する価値がありませんし、VBAはそのような、APIベースのプログラミングをするための言語ではありません。
フル3Dのオンラインゲームを作成したいのに、その媒体としてファミコンを選択する人は絶対にいません。それと同じことです。そういったゲームが作りたいのなら、そういったゲームが作成できる環境で行うべきだからです。

VBAは、決してプログラミング言語として他に見劣りするものではないと、私は断言できます。しかし、VBAでできることをわざわざAPIで行うのは、非効率ですし、なにより無意味です。APIを使っているということを自慢したいだけの、自己満足なプログラムとして捉えられても文句が言えないでしょう。

私は実際のプログラミングの際に、このことを常に考えるようにしています。VBAで実現できない、あるいはVBAである程度できるけれども、どうしても機能が足りない、そういった場合だけ、APIを使うようにしています。

APIはとても便利で有益な機能ですが、妄信して、頼りすぎてはいけません。
必要なときに、必要な分だけ、APIの力を借りることにして、これから先もプログラミングを続けていきましょう。


さて、ちょっと小難しい話になってしまいましたが、次回もAPIについて解説します。今回は結構長くなってしまったので、続きは次回、ゆっくり解説したいと思います。


■格言

APIとはウィンドウズそのものとも言える
APIを使うとVBAでは実現できないこともできる
APIは便利だが頼りすぎてはいけない


上手にAPIと付き合っていきたいものですね。






自分本位なリンクのページ

ここは、管理人影斬の、自分本位なリンクページです。

リンクに関してなにか問題がある場合は、メールフォーム等でご連絡下さい。
できる限り迅速に対応します。



■Excel関連サイト

VBAアクションゲーム?Excelで動かそう!VBAアクションゲーム?Excelで動かそう!
VBAゲームプログラマの牙城。お世話になりっぱなしです。
Excel VBAを学ぶならmougExcel VBAを学ぶならmoug
Q&A掲示板や役立つTipsが満載、スキルアップの大きな味方です。
エクセル大事典エクセル大事典
私が勝手にお師匠様と仰ぐ武藤さんのサイトです。情報がコアですごい。
猫とエクセル猫とエクセル
エクセルゲームの技術は業界一ではないかと。沢山のゲームを公開されています。
エクセル新聞エクセル新聞
非常に見やすく明るいサイト。旬な情報が満載です。
b_mode1.gifEXCEL VBA モード1本部
ここの分析結果を見ると、目からうろこ間違いなし。知らないことってあるもんだ。
ess.gifExcel Studio Sakae
VBAでサウンド関連技術ならこの人。Excel家老さんのサイトです。
no image
Office TERA
便利ソフトと貴重なテクニックが満載、寺田さんのサイトです。




■お世話になっているサイト

Abstract HopeAbstract Hope
オリジナルの楽曲素材やゲームアレンジ曲などが公開されています。
ザ・マッチメイカァズザ・マッチメイカァズ
効果音素材といえばこちら。借りまくってます……。
煉獄庭園煉獄庭園
こちらも有名な楽曲素材のサイトですね。曲数が半端ないです。
qadalia.gifqadalia
シューティングゲームを公開されているqadaliaさんのサイトです。
no image
ひしだま's ホームページ
いろいろなプログラムの情報が満載、ひしだまさんのサイトです。
no image
弾幕型
シューティングゲームの情報発信基地、本気屋さんのサイトです。
no image
もんしょの巣穴
DirectX関連の技術を盗ませてもらってます。ある意味、師匠です。




■Blogリンク

nonnkinnguそれはつまり思いつき
個人的にお世話になっている大切なお友達。のんきんぐさんのブログ。
    no image    
ってかこんなこと知ってて当然でしょ!?
PCでゲームを遊ぶための情報などを発信中。baseさんのブログ。
tasogare続・黄昏者の眠れない夜
ブログを通して知り合った黄昏者さんのブログです。
dice.gif
賽 The Die is Cast
youshi さんの気ままな日常を綴ったブログです。
no image
BLACKEYE
面白記事から映画情報まで、多彩な才能、南無さんです。
yuusan.gifにんじん牧場 ~UMANGA~
かわいらしい馬のまんが、うまんがを書かれているゆうさんのブログ。




ちなみに、等ブログはリンクフリーです。
リンクする際にご連絡等は必要ありません。
尚、相互リンクのご依頼には、必ずしも応じられませんので、ご了承下さい。
等ブログへのリンクは、テキストベースなら、こちら。

『Shadow-Slash → http://shadowslasheizan.blog114.fc2.com/』


バナーはGIF形式とPNG形式で、88×31のサイズと200×40をご用意しました。
適当に使ってください。直リンクされると、ページのアドレスが変わった場合に困ると思いますので、一度落としてからご利用下さいね。

GIFバージョン

logo01.gif

logo02.gif


PNGバージョン

logo01.png

logo02.png





■頂きバナー

EGhata.gif

上でも紹介しているqadaliaさんに作っていただきました。感謝!




その他、お問い合わせはメールフォームか、掲示板よりお願いします。






利用規約

当サイトをご利用になる方は、以下の利用規約を守ってください。

■他人の迷惑になるような行為の禁止
常識で考えて、他人の迷惑となるような行為はしないでください。これは、サイトを利用している方及び、管理者である私自身に対して双方の迷惑となるような行為の禁止を言います。このことを守れない方は当サイトを利用できません。

■コンテンツの正確性
私自身が勉強中の身ですので、全てのコンテンツにおいて、その正確性について、絶対を保障することはできません。それらに対する問い合わせ等に対して、可能な限り対応しますが、確実に対応できることを保障することはできません。このことにご了承いただけない場合は当サイトを利用できません。

■自作プログラムへの免責
上記の理由と同じで、私の作成したゲームなどのソフトに対して、その絶対の安全性を保障することはできません。このことにご了承いただけない場合は当サイトを利用できません。

■ダウンロード
ダウンロードした作品は(他サイトからダウンロードしたものであっても)完全フリーのソフトです。ご自由に利用していただけます。ただし、2次配布は禁止です。コードを公開しているものに関しては、それらの再利用や改変は自由です。そうして作ったソフトに対して、元になったソフトがあることを明確にうたう必要もありません。ただし、悪意のあるソフトに改変することは当然禁止です。
雑誌などの公的情報源となりうるものにソフトのパッケージングを行う場合は、原則報告の義務を設けます。許可無く掲載を行うことは禁止します。
ゲームなど自作ソフトをダウンロードした場合には、当利用規約全てに同意したものとみなします。

■やさしさ
どんなサイトに対しても同じだと思いますが、やさしさのない方は利用できません。当たり前ですが、大事なことです。

■利用規約の再編
予告無く、この利用規約は再編や改修が行われる場合があります。その時点において最新の利用規約を適用規約とします。






Chapter.31 [ API補足知識 ]

■前回からの続き

前回は、APIについて簡単に解説しました。
その中でSleep関数という、APIを紹介しましたね。

APIについて、できる限り簡単にわかりやすくしようとした結果、前回は最低限のことだけの解説になってしまっています。もう少しだけ、今回の講座を使って補足の解説をします。
とりあえず知らなくてもいいことではありますが、知っておいたほうが有利です。もし、今回の講座を読んでみて、一度で理解できなくても心配要りません。講座がもっと進んでから、あとで読み直してみてもいいと思います。とにかく、気負いすぎて、プログラミングが嫌いにならないでくださいね。


さて、APIは、使うために事前に準備が必要だということは、前回解説しましたね。
具体的には、モジュールの先頭部分に、APIを使うということを、宣言するコードを記述する必要がありました。それが以下のコードでしたね。

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)


まずは、これについて詳しく見ていきましょう。

コードの最初の部分を見てください。
Declare』という記述があります。
これをVBAのヘルプで調べてみると、次のように書かれていました。

ダイナミック リンク ライブラリ (DLL) の外部プロシージャへの参照を宣言します。モジュール レベルで使います。

う~ん、よくわかりませんね。
これはどういうことなのでしょう。

APIは、ウィンドウズに始めから含まれています。具体的にはウィンドウズがインストールされているドライブ(普通はCドライブです)内にある、『WINDOWS\system32』という階層に、拡張子『.DLL』のファイルとして入っています。(WindowsNT以降の場合)

このDLLファイルというのは、C言語などで記述されたプログラムを、コンパイルして出来上がるバイナリファイルです。
わかりやすく言うと、関数(機能)の集合体です。
考え方としては、コードの書かれたモジュールを、そのままファイルとして保存してあるような形です。

じゃんけんゲームを作成したときを思い出してみてください。
フォームのコマンドボタンがクリックされたときに、標準モジュール内にあるサブプロシージャを呼び出すようにコーディングしましたよね。『Call』を使って、別のモジュールにあるサブプロシージャを呼び出していました。

APIを使うということは、このプロシージャを呼び出す、ということと全く同じです。
DLLファイルの中に入っているプロシージャを呼び出して、プログラミングに使ってしまおうというわけです。
ただし、さすがのVBAでも、得体の知れないDLLファイルから、関数を呼び出すことはできません。そこで『Declare』の出番です。

Declareステートメントは、○○というDLLの中にある○○という関数を使うよ! とVBAに教えてあげる役割を担っているのです。
これがわかると、なんとなくあの意味不明なコードも、少しだけ意味がわかってきませんか?

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)

さて、問題のSleep関数のDeclare宣言部分をもう一度見てみましょう。

Declareに続いて、『Sub Sleep Lib "kernel32.dll"』と記述されています。
Sleep関数は、先ほどの『WINDOWS\system32』に入っている『kernel32.dll』に含まれている関数です。それをここでVBAに教えているのですね。
日本語訳してみると『kernel32.dllというDLLファイルに含まれている、Sleepという関数を使うよ!』っていう感じですね。

そして次には、括弧で囲まれた『ByVal dwMillsecounds As Long』という記述があります。ここで重要なのは最後の部分、『As Long』のところです。
引数として、Long型のデータを渡す必要があることがわかります。文字の『1』とか、小数点を含んだ『0.1』とか、整数ではないデータをSleep関数に使うことはできないのですね。

APIの種類によって、宣言文の形はいろいろあるのですが、基本的にはこれと同様なルールで全て記述されます。
原則として、使いたいAPIの宣言文を調べて、それをそのままコピーするだけでいいので、宣言文のコードを書き換えたりする必要はありません。というか、書き換えてしまうと、正しく動作しません。大文字と小文字などの違いも、基本的に許されません。一字一句間違えずに記述する必要があります。
ですから、大抵はコピーしてしまうのが一番確実で、簡単です。

自分が使いたいAPIの宣言文さえ見つかれば、それをコピーして貼り付けるだけでAPIを使うことができるのですね。簡単、簡単。

ただし、引数の型の問題など、宣言文の意味を知っていることでわかることもあるので、参考までに解説しました。APIは、引数のデータ型を間違えてエラーや問題が発生する場合が多いですのでね。


■そこでSleep関数登場

さぁ、少し難しい内容ですが、ついてこれているでしょうか。

APIは、ウィンドウズにとって非常に重要な役割を果たしています。普段は意識せずとも、常に皆さんがパソコンを操作するときに、影で活躍しているのです。
そんな縁の下の力持ちであるAPIのひとつ、Sleep関数について、もう少し詳しく解説しましょう。

Sleep:

構文
    Sleep 処理を止めたい時間
説明
    処理を止めたい時間をミリ秒で指定する
    引数はLong型、つまり整数で指定する

先ほども書いたように、Sleep関数は、引数として整数型のデータを必要とします。これが実は結構重要。

厳密に言うと、Sleep関数は小数点以下の数値が含まれたデータや、文字列の数字など、本来整数ではないデータを渡しても、場合によってはエラーを起こしません。
しかし、この引数の型の問題は、しっかりプログラマーのほうで意識しておかなくてはいけません。

全てのAPIが、Sleep関数のように、データ型の違いを吸収して正しく動くとは限りません。あらかじめ、引数の型をしっかりと把握しておき、正しく指定するようにしましょう。
今後、様々なAPIを扱うことになると思いますが、これは非常に大事なことなので忘れないようにしてくださいね。

次に、具体的なSleep関数の機能について考えてみます。
これに関しては、少しだけ難しい話なので、わからない人は、無理に理解する必要はありません。興味がある人は、ちょっと我慢して次の項を読んでみてください。


■マルチスレッドとSleep関数

ウィンドウズは、マルチスレッドという仕組みを持っています。これは、一度に複数のプログラムが動くことができる仕組み、というのが一番わかりやすいのではないかと思います。
この仕組みがあるおかげで、例えばWord(ワード)とExcelを同時に起動したとしても、問題が発生することはありません。
また、ウィンドウズメディアプレーヤーなどで音楽を再生しながら、テキストエディタで文章を編集することも、全く問題なくできますね。これも、このマルチスレッドという仕組みのおかげです。

その他にも、コンピューターウィルスの侵入がないかを監視するセキュリティソフトなどは、このマルチスレッドがないと、あまり役に立たなくなります。
なぜなら、インターネットを閲覧するために、そのためのソフトが起動している必要があるからですね。複数のプログラムが同時に動けないとなると、インターネットを見ながら、同時にウィルスをチェックすることはできなくなってしまうわけです。

このように、ウィンドウズがマルチスレッドという仕組みで管理されていることにより、ユーザーは快適に、わかりやすく、安全なパソコンライフを満喫できるわけですね。

この仕組みをもう少し理解するために、ひとつソフトを紹介したいと思います。

ウィンドウズには、『タスクマネージャ』というソフトが標準で入っています。おそらく、ウィンドウズをお使いの皆さんのパソコンにも、入っているはずです。
タスクバー上で、右クリックすると表示される、コンテキストメニューから起動することができます。

160.gif

このタスクマネージャ、ウィンドウズが今どんなソフトを起動しているのかや、メモリの状態など、様々な情報を教えてくれる便利なソフトです。
タスクマネージャを起動して、『パフォーマンス』の項目を選択すると、CPUがどのような状態なのかグラフで表示され、視覚的にCPUの現在の状態や履歴を確認することができます。

161.gif

ゲームなどの重たい処理を行っているときには、CPUの使用率が高くなるのが目に見えてわかります。一生懸命パソコンが考えているのが見ていてわかるので、なんだかちょっと面白いです。

VBAがコードを実行している間は、CPUが可能な限りフルパワーで使われています。その証拠に、繰り返し処理などの最中に、タスクマネージャーを起動していると、こんな感じになります。

162.gif

CPU使用率が100%になってますね。
パソコンが、できる限りの力を出して、処理を行っているのがわかります。
実はSleep関数は、こんなときに活躍してくれるのです。

人間も、全速力で走り続けたら、いつかは限界が来ます。パソコンだって同じです。
パワー全開で処理を続けることは、あまりパソコンにとって優しくありません。何時間もほったらかしにしておいたら、きっといつか壊れてしまうでしょう。
Sleep関数は、呼び出されると同時に、引数で指定された時間だけ、現在の処理を中断します。その間、CPUは、他の処理を行ったり、休憩(ちょっと語弊がありますが)を行うことができます。難しい言葉で表すと、スレッドを中断させるわけです。

ですから、繰り返し処理の最中などに、Sleep関数を呼び出すことにより、パソコンに『これだけの時間、他のことをやったり、休んだりしててもいいよ』と命令することができるのです。
ウィンドウズはマルチスレッドです。できるだけ、ひとつのプログラムが処理をひとりじめしないようにしたほうが、パソコンには優しいのです。Sleep関数が、現在のスレッドを中断するということは、VBAの処理の最中に、そのスレッドを中断して他のスレッドに処理を分け与えるということなのですね。

処理を停止する時間をミリ秒単位で指定する、というのは、実はこういうことだったのです。
Sleepは『眠る』という意味ですから、なんとなくわかりやすいですね。

内容的にかなり難しい話でしたが、なんだか自分の説明が不足しているような気がするので、理解できなかったらすいません。


■まとめ

いやぁ、かなり長いテキストになってしまいました。しかも難しい話ばかりで、ちょっと疲れてしまったかもしれません。

今回の講座で大事なことは、APIの宣言文の意味、特に引数についての理解です。
APIは便利ですが、その分、扱いが難しく、予期せぬ結果を招きやすいです。引数の型の問題は、結構よく起こる問題のひとつだと思いますので、しっかり理解しておいて下さい。

途中、マルチスレッドの話など、ちょっと難解な内容が出てきましたね。この辺のことは、私も実際よくわかっていないので、専門的な知識を持った方から見ると、語弊や間違いがあるかもしれません。
そういったときには、ご連絡をいただけると助かります。私自身、まだまだ勉強中の身です。皆さんも、諦めずに、プログラミングに取り組んでくださいね。


■格言

Sleep関数の引数には整数型
パソコンに負荷をかけすぎないためにSleep関数を活用


ゲームは一般に処理が重いですからSleepは重要です。






基礎講座1:ExcelとVBA

基礎講座1では、まずExcelについて簡単に触れていきます。
そして、隠れたExcelの基本機能であるVBAについて解説します。

Excelでゲームを作るということは、ほとんどの場合このVBAを使ってゲームを作ることを指します。もちろん、それ以外の方法もありますが、やはり、自由度の高い設計を行うのなら、VBAは避けては通れないでしょう。
基礎講座1ではこのVBAとはどんなものか、また、それを起動する方法から基本的な使い方までを丁寧に説明していきます。

講座の中でも言っていることですが、Excelでゲームを作るのに、特別なソフトは何も要りません。Excelが使える環境、それだけが条件です。考えてみるとこれは相当すごいことではないでしょうか。
ゲームを作ってみたいと思っている多くの人は、プログラミングをするために必要な環境を持っていません。それらの環境を整えるまでに気持ちが萎えてしまうことも多いでしょう。Excelでゲームを作るということは、これらのわずらわしさが一切ありません。ただ目の前に、Excelがあるのなら、あなたは今すぐにプログラミングを始めることができるのです。これこそが、Excelでゲームを作ることの最大の利点だと思うのです。
私自身、ゲームを作りたい衝動だけが先走って、実際にはどうしたらいいのかわからなかった一人です。もし、これをご覧のあなたが、同じように考えているのなら、何をどうしたらいいのかわからずに、悶々とした日々を過ごしているのなら、是非Excelでプログラミングを始めてみてください。きっと思い通りのゲームを作れる日が来るでしょう。私は、私なりのやり方で、そのお手伝いができればいいなと思っています。

さて、前置きが長くなりましたが、頑張って始めてみましょう。







基礎講座2:基本的なコーディング

基礎講座1では、ExcelやVBAの起動や各部の説明ばかりでしたが、基礎講座2では、実際のコーディングを本格的に始めていきます。

どのようなプログラミングにも言えることですが、コードを書き終わって、実際に動かしてみた際に、自分が思っていた通りに動くということは、まずありません
まっすぐ正面に進んでいくはずのキャラクターが、何故か後ろにぶっ飛んでいって画面から消える……なんてのはよくあることです(経験者は語る)。
自分では気がつかなかったようなバグが、他人の指摘によって明らかになることも多いですし、絶対にバグのないコードなんて、恐らく無いのだと思います。なぜなら、日本全国いたるところで使われているパソコンが、全て同等の性能ということはありえませんし、環境も全く違っているはずだからです。
Excelのバージョンも違うでしょうし、ウィンドウズだけがパソコンではありませんし、どんなソフトがインストールされているのかもわかりません。
これらのことを踏まえて考えると、プログラミングとは、地道で、忍耐力の要る作業であることがわかると思います。ひとつの完成したプログラムには、たくさんの地道な努力と、果てしないバグとの戦いが詰まっているのです。

これは、残念ながら、プログラマーにとっては宿命のようなものです。基本的にコードにはバグがあるものなのです。それを受け入れられない人は、プログラミングをするべきではありません。出来上がったソフトが、バグだらけで人に迷惑をかけるものであってはならないからです。

これから実際にコードを書いていくことになりますが、私の紹介するコードは、自分のパソコンでは正常に動作したということ以外、何の保障もできません。そのかわり、極力どんな環境でも動作するコードを紹介していくように、私なりに努力します。
そして、できる限り皆さんの助けとなるようなコードを紹介できるように、私も努力を続けていくつもりです。

さぁ、それでは頑張っていってみましょう。


    Chapter.12 [ 乱数 ]

    Chapter.13 [ 条件分岐 ]

    Chapter.14 [ ゲーム画面のデザイン ]

    Chapter.15 [ コード記述の基本作法 ]

    Chapter.16 [ じゃんけんゲーム:1 名前をつける ]

    Chapter.17 [ じゃんけんゲーム:2 フォームの起動 ]

    Chapter.18 [ じゃんけんゲーム:3 乱数の種 ]

    Chapter.19 [ じゃんけんゲーム:4 イベント ]

    Chapter.20 [ じゃんけんゲーム:5 引数 ]

    Chapter.21 [ じゃんけんゲーム:6 役判定 ]

    Chapter.22 [ じゃんけんゲーム:7 予測と制限 ]







基礎講座3:発展系基本技術

基礎講座2を完了した皆さん、お疲れ様でした。
じゃんけんゲームはうまくできたでしょうか。

基礎講座3では、基本的な技術ではあるけれども、少しだけ発展した技術を解説していきます。
この講座の目的は、『基礎の拡充』です。
基礎講座2までで、おおよそ基本的なことは学習できています。しかし、基礎講座3で紹介する内容をしっかり理解していると、様々な面で役に立つこと間違いなしです。

後半では少しだけ難しい内容を紹介しますが、それなりに見栄えのするゲームを作るためには、絶対に必要な内容となっています。
できるだけわかりやすく解説しますので、諦めずにしっかり取り組んでくださいね。

最初は理解できなかったことでも、不思議と何度も目を通す間に、理解できるようになってきます。少なくとも、私はそうです。
大抵の技術は、一発で理解することが難しい。しかしながら、何度も調べたり、テストしたりを繰り返す間に、自然と理解できるようになっています。不思議ですね。

大切なことは、何度も言ってますが、第一に諦めないこと。
そして、自分でいろいろいじりながら、実際にコードを実行させてテストしてみることだと思います。
テキストや解説を眺めているだけでは、なかなか頭に入ってきません。今回解説する内容も、まずは付属のコードをそのままコピーすることから始め、理解を深めるためにも自分で積極的に改変してみてください。コードを実際に動かしてみて初めてわかることもあります。間違いを恐れず、失敗を恐れず、頑張ってくださいね。

基礎講座は、今回の3までで終わって、次からは実際にゲームを作るための実践的な内容に移っていきます。そこで足踏みしないようにするためにも、基礎の最後をしっかり締めくくりましょう。







サイトマップ


    ■はじめに:TOPページ■
        TOPページへはこちらから。

    ■基礎講座1:ExcelとVBA■
        Excelの基本的な機能やVBAについて簡単に解説します。
        Excelに不慣れだったり、本当の基礎から学ぶならこちら。
        Chapter.1~.11までの全11回。

    ■基礎講座2:基本的なコーディング■
        ここから少しずつコードを書いていきます。
        後半では実際に簡単なゲームを作ってみます。
        Chapter.12~.22までの全11回。

    ■基礎講座3:発展系基本技術■
        少し発展した便利な機能や技術を解説。
        ここの内容を理解しているかどうかが、実は重要。
        Chapter.23~.33までの全11回。

    ■実践講座1:動きのあるゲームをつくる■
        ユーザーフォームを利用して、動きのあるゲームを作ります。
        最終的にはシューティングゲームが完成する予定。
        Chapter.34~.52までの全19回。

    ■実践講座2:演出を考える■
        シューティングゲームを通して、演出を考えます。
        すこし凝ったことをしたいときとかに参考になるといいんですが。
        Chapter.53~.67までの全15回。

    ■実践講座3:知っておきたい基本■
        知っておきたい基本をあらためて取り上げます。
        さらなるレベルアップ、基礎の底上げに取り組みましょう。
        Chapter.68~.82までの全15回。

    ■作成講座1:タイピングゲーム■
        作成講座の第一弾はタイピングゲームです。
        タイピングゲームの作成を通して、文字列に関するテクニックを習得しましょう。
        Chapter.83~.95までの全13回。

    ■作成講座2:クリックゲーム■
        作成講座の第二弾、クリックゲームです。
        クリックゲームの作成を通して、クリックに関するイベントをマスターしましょう。
        Chapter.96~.105までの全10回。

    ■作成講座3:ブロック崩しゲーム■
        作成講座の第三弾はブロック崩しゲームです。
        ここではベクトルなどのちょっと難しいけれど使えたら便利なテクニックを解説。
        Chapter.106~.116までの全11回。

    ■上級講座1:一歩上いくテクニック■
        ファイル操作やクラスモジュールについて解説。
        一歩上のVBAテクニックを取り扱います。
        Chapter.117~.128までの全12回。

    ■上級講座2:APIを用いた描画処理■
        WindowsAPI を使って描画を行なう処理について解説。
        いわゆる、モード4と呼ばれているタイプの処理。
        Chapter.129~.139までで、現在執筆中。

    ■自作フリーExcelゲーム紹介■
        自作のフリーゲームなどを置いています。
        ご自由にダウンロード下さい。
        感想とかいただけると、作者はとっても喜びます。

    ■利用規約■
        ブログの利用規約です。
        はじめにご一読下さい。

    ■リンクについて■
        リンクについてまとめています。
        Excelの有益な情報を公開されているサイトや、
        お世話になっているサイトへのリンクも。

    ■影倉庫(別館)■
        自作ゲームはもちろん、サンプルや素材など、
        様々なものを保管している影の倉庫です。

        質問などを受け付けるサポート掲示板もこちらにあります。

        サポート掲示板へ

        お気軽に質問などをお寄せください。







Chapter.32 [ メインループを考える ]

■ゲームを作るために

今回は、いよいよ実践的なゲーム作りの内容へ移行します。
基本的な技術の話ばかりで中々具体的な話が出てきませんでしたので、きっと皆さんも痺れを切らしているのではないかと思います。

プログラミングに限らず、どんなことでも基礎が大事だと言いますね。今までの内容をしっかり理解してきてくださった皆さんなら、この先の内容もきっと理解できるはずです。焦らずがんばっていきましょう。

今回のテーマは『メインループ』です。
しかし、聞きなれないこのメインループというのは、一体どういうことを指すのでしょうか。まずはそこから見ていきましょう。


メインループというのは、動きのある演出には欠かせない概念です。これを理解していれば、動きのあるゲームを作成することができるようになります。
以前作成したじゃんけんゲームは、特に動きに関する演出が施されてはいませんでした。見た目が非常に地味で、簡素な内容のものでしたね。しかし、今回から解説するメインループの仕組みを実装させることで、動きのある、見た目にも楽しいゲームを設計できるようになるでしょう。

動きのあるゲームというのは、テレビアニメの仕組みと同じ概念で作成します。少しずつ表示される絵柄や位置を変更することで、あたかも動いているように見えるのですね。
少しずつ動かしたり、絵柄を変更したりするので、これには『繰り返し処理』が役立ちます。1回の処理で右に1ポイントずつ動くようにして、これを繰り返し処理すれば、まるで右に向かって移動しているように見えるでしょう。
また、そのとき毎回表示される絵を変更すれば、まるで人が歩いているように見せることだってできるはずです。

このように、ゲームが起動している間、常に繰り返し処理を行い続けることで、キャラクターを様々に動かしていくわけです。
この、常に行い続ける繰り返し処理を『メインループ』と呼んでいます。今回はこれについて解説していくことになります。


■最も簡単なメインループ

さて、メインループの概念は理解できましたか?

では実際に、簡単なメインループを組むとしたら、一体どのような形になるのでしょうか。

繰り返し処理を使ってループするわけですから、『For文』か『Do文』を使えばよさそうですね。
ただ、ゲームの場合は、プレイヤーが始めたいときに始まり、終わりたいときに終わる、というのが普通ですよね。例外としてゲームオーバーやゲームのクリアなどがあるとはいえ、これに関しても、毎回一定のタイミングがあるとは限りません。
つまり、繰り返す回数が一定ではないということですね。ということは、繰り返し処理に『Do文』を使ったほうがよい、ということになります。以前にも解説したように『For文』は繰り返す回数を指定する必要があるので、メインループの繰り返しには適していません。

それではDo文を用いて、メインループの概念を考えてみます。

Do文は、なにかしらの終了条件を定義していないと、永遠に止まりません。これは以前にも解説しましたね。
ゲームが永遠に止まらなくなってしまっては困るので、まずはその仕組みから実装しましょう。それが以下のコードです。

Sub Main()
    Dim Flg As Boolean
    Flg = False
    Do Until Flg
        'ゲームの処理
    Loop
End Sub


始めから順に見ていきます。

まず1行目。このプロシージャがメインループになることを想定して、それがわかりやすい名前をつけておきます。今回は『Main』というプロシージャ名にしました。
次に2行目。Boolean型の変数、『Flg』を宣言しています。Boolean型は『』と『』の2種類の属性しか持っていない変数の型です。わかりやすく言うと、正しいときと、正しくないとき、という2択をする場合に適した変数ですね。
次、3行目です。ここでは、上で宣言したBoolean型の変数であるFlgに『False』を代入しています。ちなみにFalseは『』を表します。
これは余談になりますが、Boolean型の変数の規定値はFalseです。ですから、あえてここでFalseを代入しても何も変わりません。しかし、プログラミングの世界では、自分が意図しない結果を招いてしまうことのないように、あらかじめ安全を考慮したコードを書く癖がついていたほうがいいです。ですから、あえて始めに変数にFalseを代入して初期化しています。

さて、メインループの肝となる4行目を見てみましょう。
ここでは、Untilを使って、ループ処理の終了条件を記述しています。
Do Until Flg』というのは、一見すると意味がわからないかもしれませんね。終了条件に、先ほどの変数名だけが記述されていて、まるでどういう条件になっているのかわかりにくいですね。これにもちょっと補足説明が必要です。

例として、以下の2つのコードを見てください。

Bool はBoolean型の変数です

    ① If Bool = True Then

    ② If Bool Then

実はこの2つの条件分岐処理は、全く同じ意味です。えっ、なんで? と思いますよね。
①では、BoolイコールTrueのときは……、という指定の仕方をしているのに対し、②のほうはただ単にBoolと記述されているだけです。なんとなく変な感じがします。
思い出してみてください。If文を使った条件分岐は、正しいときと、正しくないとき、という2択を行います。
これ、さきほどもどこかで出てきませんでしたか?

If文 ⇒ 『』か『』かで条件分岐処理を行う

Boolean型 ⇒ 『』か『』かどちらかの属性を持つ変数型

このように、条件に関する処理と、Boolean型は非常に相性がいいのです。
If文は、条件を判断するとき、内部的にはその条件が『』なのか『』なのかを判断しています。ですから、条件にBoolean型の変数を与えるだけで、条件の判断が完了してしまうのです。
これにより、わざわざ『Bool = True』とイコールを使って比較する必要はなくなります。

メインループのUntilでも、これと全く同じことが起きています。Untilが終了条件を判断するときにも、内部的に終了条件が『』なのか『』なのか判断しています。ですから、判断する条件としてBoolean型の変数を与えるだけで、終了条件の指定はOKなんですね。

さて、繰り返し処理の終了条件は指定できましたので、あとはDo~Loopの間にゲームの処理を記述すればいいだけです。
ではゲームの処理を中断、あるいは終了したいときにはどうすればいいでしょう。これはここまでキチンと呼んできていれば簡単ですね。

Untilの終了条件で、Flgを指定していますので、繰り返し処理を終了したいときには、FlgTrueを代入すればよいのです。

これが、最も簡単なメインループになります。まぁこのままでは完成度としてはまだ80%ですが。


■メインループの完成度を高める

さきほど完成度が80%と言ったのにはわけがあります。

前回の講座を思い出してください。
タスクマネージャというソフトを起動しておいて繰り返し処理を実行したとき、パソコンがどのような状態になるか観察することができると解説しましたよね。

具体的には、繰り返し処理を行っている間はCPUがマックスパワーで動き、CPU使用率が100%になってしまうのでしたね。この状態はあまりパソコンにとってよくないので、回避できるものなら、何とか回避しておきたいですよね。
そしてこの問題を解決するために、APIのひとつであるSleep関数が役立つということも、やんわりと解説しました。

Sleep関数が呼び出されると、Sleep関数の引数として指定された時間だけスレッドが中断され、その間はパソコン(CPU)が休憩できます。これにより、CPU使用率が下がります。
ですから、繰り返し処理の中でSleep関数を毎回呼び出して、少しずつ休憩しながら繰り返し処理を行うようにすれば、パソコンにとって負荷が少なくなります。

それを踏まえて、先ほどのメインループのコードを書き換えると、次のようになります。

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)

Sub Main()
    Dim Flg As Boolean
    Flg = False
    Do Until Flg
        'ゲームの処理
        Call Sleep(1)
    Loop
End Sub

このようにして、ゲームの処理が1回ループするたびに、ほんの一瞬だけ処理を中断させるようにします。こうしておけば、CPU使用率が100%になるという危険な状態は、ちゃんと回避できます。処理を中断しているのはたった1ミリ秒ですが、これだけでも充分です。(内部的には、厳密に言うと1ミリ秒ではないのですが、その話はまたどこかで)

これで、メインループは完成度90%になりました。

……え?
……なんで100%じゃないの?
って思いますよね。でも、完成度を100%にするためには、もう少しだけ理解しなければいけないことが残っています。そして、結構長い話になってしまいますので、次回に続く、です。
とりあえず、今回はここまでにします。
内容的には結構濃かったですから、焦らず今回の内容をしっかり理解しておいてくださいね。ここの内容をしっかり理解しておくことが、きっと次回以降役に立つはずですからね。


■格言

メインループとはゲームの根幹となる繰り返し処理
Do Untilの終了条件にはBoolean型を使う
APIのSleep関数を併用してCPUに配慮


今回の内容は重要なことばかり。しっかり覚えておいてください。







Chapter.33 [ 同期処理の概念 ]

■同期処理とは何か?

基礎講座3では、『Withステートメント』に始まり、『画像表示に関する処理』、『繰り返し処理』、『APIについて』と、解説してきましたね。
そして前回のテーマだった『メインループ』。これは前回、完成度90%という中途半端な状態で終わってしまっています。

長々と基本について解説してきたのにはちゃんと理由があります。今回のテーマである『同期処理』を理解するために、どうしてもこれらの解説が必要だったからです。ここまで、我慢強く進めてきた皆さん、本当にがんばっていると思います。
皆さんはもう十分な基礎力がついています。今回の講座もきっとついてこれるはずです。もう少しだけ、基本に忠実にがんばりましょう。

さて、今回のテーマ『同期処理』ですが、皆さんはなんのことだかわかるでしょうか。

ゲームのプログラミングを行うにあたって、同期処理というのは実はすごく重要な処理です。これをしっかりと実装していないゲームは、下手をするとゲームとして成り立たないほど、ひどいものになってしまいます。
これはアクションゲームや、シューティングゲームなど、動きの激しいゲームであればあるほど顕著に表れます。

同期処理というのは、どのようなパソコン環境でもできる限り同じように処理が行われるように、ゲームの動きを調整する処理のことです。
ちょっとわかりづらいですね。解説します。

例えば、世界最強の処理能力を持つ、スーパーコンピューターがあったとします。Excelを100個同時に起動してもへっちゃらなほど、高いポテンシャルを誇ります。
一方、その隣には、10年ほど前に製作されたちょっと古いマシンがあります。コイツはちょっと重たいソフトを起動したら、フリーズしたかと思わず勘違いしてしまうほど、低いスペックのマシンです。

当然ですが、この2つのマシンが同じ処理を行う場合、完了するまでの時間には差が出ます。簡単な処理であれば、見た目にはほとんど違いがないかもしれません。しかし数千回、数万回ループする処理など、非常に重たい処理を行う場合には、それなりに大きな時間の差が出てくるでしょう。
考えてみると、これは非常にやっかいです。

シューティングゲームの場合、プレイヤーが操作する自機や敵キャラクターの動く速さはもちろん、敵が撃ってくる敵弾の速さ、これがパソコンの性能によって変わってしまうということです。
かたや、超高速で襲い来る恐怖の弾幕。かたや、ゲームに集中できないほどの超低速弾……。これじゃいけませんね。

これは極端な例ですが、そんなに現実離れした話でもありません。
パソコンなどの機器は進化が非常に早いですから、2~3年前のパソコンでも、結構処理能力やスペックに差があるものです。

私たちが作ったゲームは、どのような環境でプレイされるかわかりません。環境によってゲームの処理速度、つまり難易度が変化することがあるのは好ましくありません。私たちプログラマーが、動作環境の違いによって処理が変化しないように、意図的に調整しなくてはならないのです。
これこそが『同期処理』なのです。


■ではどのように実装させるか

さぁ、問題はこれです。いかにして同期処理を実現すればいいのでしょうか。

ポイントとなるのは、パソコンの処理速度が速くても、遅くても、同じように処理が進むようにすることですよね。これはカメとウサギに例えて考えてみましょう。

カメさんは、とってもゆっくりです。それに対して、ウサギさんは非常に速い。
同時に走り出したら、あっという間にウサギさんが先に進んでしまい、カメさんは置いていかれてしまいます。
しかし、ウサギさんが途中で休んでしまったが為に、ゆっくりカメさんは、結局ウサギさんに追いつくことができました。


どうですか、なんとなく同期処理の方法が想像できませんか?

鍵を握っているのは、『速すぎたときには処理を止めて待つ』という仕組みです。
処理が行われている最中、ある一定の時点で速度をチェックをして、速く進みすぎている場合には、処理が止まるようにすればいいわけです。

どうですか? なんとなく考え方はわかったでしょうか。
この考え方を取り入れながら、前回解説したメインループに同期処理を組み込んでみましょう。同期処理が組み込めたら、晴れてメインループ完成度100%に到達です。


■同期処理に必要なAPI

同期処理を行うためには、Sleep関数に続く、新たなAPIをひとつ紹介しなくてはなりません。
そのAPIは『GetTickCount』というAPI関数です。
宣言文は以下のようになります。

Declare Function GetTickCount lib "kernel32.dll" () As Long

このGetTickCount関数は、ウィンドウズが起動されてから、どのくらい時間が経っているのかを教えてくれる機能を持っています。その時間の単位は、Sleep関数と同様、ミリ秒単位となっています。

このGetTickCount関数をうまく取り入れることによって、同期処理が実現できます。

まず、宣言文をよく見てみましょう。
Sleep関数のときと同様、Declareステートメントを使っていますね。これはどんなAPIを使うときでも必ず同じです。そして注目すべきは括弧の部分です。
Sleep関数は、処理を中断する時間を、整数のデータとして引数に指定していました。しかし、GetTickCount関数の括弧の中には、何も書かれていませんね。空の括弧がついているだけです。
このことから、GetTickCount関数は引数を必要としないということがわかります。GetTickCount関数は情報を取得するという目的のためだけの関数ということですね。

実際に、この関数は簡単にテストすることができます。ためしに、下記のコードを実行してみてください。すると、何らかの数値がメッセージとして表示されるはずです。

Declare Function GetTickCount Lib "kernel32.dll" () As Long

Sub Get_Time_Test()
    MsgBox GetTickCount
End Sub

なんだか異様に大きな数値が表示されたのではないでしょうか。この数値が、ミリ秒単位で表した、起動してから現在までの時間なんですね。
例えば『600000』と表示された場合には、どれくらいの時間が経っているかというと……

1秒が1000ミリ秒なので、
    600000 ÷ 1000 = 600 = 600秒
1分は60秒なので、
    600 ÷ 60 = 10 = 10分

そう、10分経っているのです。
このように、GetTickCount関数は、ウィンドウズを起動してから現在までの時間を教えてくれます。この仕組みを使って、ゲームの同期処理を行います。


■完成度100%メインループ

メインループに同期処理を組み込むために、GetTickCount関数を使いますが、それと同時に、Long型の変数を新しく宣言します。

ゲームの繰り返し処理が行われるとき、まず始めに、この新しく宣言したLong型の変数にGetTickCount関数が教えてくれる数値を入れておきます。
こうすることで、そのときのループがいつ始まったのか、あとで知ることができますよね。ウィンドウズを起動してから、ちょうど10分経っていたとしたら、このとき変数には『600000』という数値が入るはずです。

そして、ゲームの処理が全て終わって、繰り返し処理の最後の部分に差し掛かった段階で、もう一度GetTickCount関数を呼び出します。すると、ゲームの処理にかかった分だけ、時間が進んでいるはずですよね。先ほど変数に入れておいた数値と比較すれば、どのくらい時間が掛かったのかがわかるはずです。
例えば最後にもう一度GetTickCount関数を呼び出した時に、教えてくれる数値が『600500』になっていたとしたら、変数に入っている数値と比較すると『500ミリ秒』つまり、0.5秒時間が経っているということがわかります。

もし、1回のループを1秒単位で回したいと考えていた場合には、これでは処理が速く終わりすぎてしまっています。つまりウサギさん状態ですね。
こんなときは、ウサギさんには少し待ってもらいましょう。そのために、もうひとつDo~Loopを用意して、その中でぐるぐる回ってもらいます。

なかなかイメージしづらいと思いますので、完成版のメインループのコードを載せちゃいます。これを見ながら、もう少し踏み込んで考えてみましょう。コードは次のようになります。

Sub Main()
    Dim Flg As Boolean
    Dim Stm As Long                 'Long型の変数を新たに宣言
    Flg = False
    Do Until Flg
        Stm = GetTickCount             '始めに時間を取得しておく
        'ゲームの処理
        Do
            Call Sleep(1)
        Loop Until GetTickCount - Stm > 1000     '比較を行って速すぎたらループする
    Loop
End Sub

わかりやすくするために、コメントを併せて追加してみました。
少しコードが複雑なので、ちょっとわかりづらいですが、よく見てください。

2番目に登場する、中に入っているほうのDo~Loop文で、処理が速すぎていないかチェックしています。GetTickCount関数で取得できる数値から、始めに変数に確保した数値を引き算します。先ほどの例で言えば『GetTickCount関数で取得する数値 - 600000』という計算をしているのですね。
この計算の結果が、『1000よりも大きくなったとき』というのが終了の条件になっています。ですから、もし処理が速すぎる場合(計算結果が1000より小さい場合)はループし続けるわけです。

ゲームの処理に500ミリ秒しか掛からなかったとしたら、引き算した結果が1000よりも小さくなります。こんなウサギさんにはDo~Loopという檻の中で、適切な時間が来るまで延々と回ってもらいます。
こうすることによって、ゲームのメインループは、任意の時間で1回ずつ処理が行われるようになるわけです。ウサギさんがぐるぐる回っている間に、無駄な負荷が掛からないようにするために、Sleep関数を中にあるDo~Loop文に仕込むというのがポイントですね。


■まとめ

さて、結構難しい内容でしたね。どうでしょう、私の説明が不足でないか心配ですが、これにてめでたくメインループの完成度が100%に到達しました。

このメインループは、今後雛形として使います。その構造について正しい理解が必要です。例えば1回のループ時間を、1秒単位ではなく、もっと短い時間で指定したい場合はどうすればいいか、皆さんはわかりますか? キチンと内容が理解できていれば、これは簡単なはずです。

2番目のDo~Loop文で指定されている終了条件、『Loop Until GetTickCount - Stm > 1000』の時間を変更すればいいのですね。
先ほどの例の場合では、計算結果との比較として1000を指定しています。これは1000ミリ秒ごと、ということですからすなわち1秒ごとに相当しますね。もし、もっと速くループを回したい場合には、この1000の部分をもっと小さな数値にすればいいだけです。
ここで500を指定すれば0.5秒ごとにループしますし、100を指定すれば0.1秒ごとにループします。理屈がわかってしまえば簡単ですね。

今回解説した『同期処理がキチンと取れているメインループ』は、動きのあるゲームを作る際には常に必要になる仕組みです。私が作成するゲームも、この仕組みを採用しているものがほとんどです。
ほぼ完成された形ですから、先ほども書いたように、今後、このメインループを雛形として解説を続けていくことになります。難しい内容ではありますが、しっかり覚えて、次回以降に備えてくださいね。


■格言

同期処理でどんな環境にも対応させる
GetTickCount関数はウィンドウズ起動からの時間をミリ秒単位で教えてくれる


繰り返し処理+同期処理+Sleep、これで完璧メインループ。






Chapter.34 [ 移動処理その1:画面設定と考え方 ]

■いよいよ実践編スタート!

実践講座の第1回目となる今回からは、いよいよキャラクターを動かす処理を考えてみたいと思います。

以前の講座で、ユーザーフォームに画像を表示させるためのテクニックを解説しましたね(Chapter24Chapter25を参照)。このときは、ユーザーフォーム上にただ画像を表示させているだけでしたが、今回はそれを動かす方法を解説します。

画像を動かす、ということは、正確には『画像をロードしたイメージコントロールを動かす』ということを表します。
イメージコントロールに、自分で任意の画像をあらかじめロードしておいて、それをキャラクターに見立てます。イメージコントロールが表示される位置を変化させると、まるでキャラクターが動いているように見えるというわけですね。

今回は、まずそのとっかかりとして、まっすぐ進むだけの単純な移動を行ってみましょう。ただ直進するだけ、といっても、結構考えることはあるものです。油断せず、しっかり覚えてくださいね。


今回は、単純な移動処理を行うだけですが、皆さんが実際にゲームを作るときに即役立つようにするためにも、キチンとキャラクターを表示させてやってみることにしましょう。
というわけで、今回は以下のような手順で進めます。

①あたらしくユーザーフォームを用意する
    ▼
②ユーザーフォームにイメージコントロールを配置する
    ▼
③イメージコントロールに画像をロードする
    ▼
④フォームにコマンドボタンを配置する
    ▼
⑤イメージコントロールを動かすコードを用意する
    ▼
⑥コマンドボタンが押されたときに、⑤のコードが始まるようにする



■まずはフォームの準備から

さて、前述の①~⑥までの手順のうち、④までの4つの手順は、以前の解説をキチンと理解していれば別段問題なくできるはずです。
ですから、ここでは簡単な説明に留めます。

まずは、VBAのメニューから『挿入』→『ユーザーフォーム』と選択します。これで新しいユーザーフォームがプロジェクトに追加されるはずです。
次に、ユーザーフォーム上に、適当な大きさで構わないのでイメージコントロールをひとつ、配置しましょう。これで①と②が完了ですね。

次は今配置したイメージコントロールに、画像をロードします。これには、イメージコントロールの『Pictureプロパティ』を使うんでしたね。
今回はサンプルとして、次のような画像を用意しました。講座中はとりあえずこの画像を使って説明していきます。ちなみにこの画像のサイズは『24×24ピクセル』です。

Red.gif

画像は自前で用意したものでも構いません。ただし、その場合には注意しなければならないこともあります。それは画像の大きさです。
今回解説するサンプルコードなどは、上で紹介している画像を想定したコードになります。自前の画像を使用する場合には、イメージコントロールの大きさや、その他の処理で使う数値などを、適切に変更する必要がありますから注意です。

よくわからないという人は、とりあえず上の画像を使ってみてくださいね。上の画像を右クリックして『対象をファイルに保存』を選択すれば、ダウンロードすることができます。

とにもかくにも、無事画像が準備できたら、イメージコントロールのPictureプロパティを使って画像をロードしましょう。
無事に画像がロードできたでしょうか。ここまでで、次のような状態になっていればOKです。

170.gif

このままでは、ご覧のとおりゲームに使える状態ではないので、イメージコントロールのプロパティをいくつか変更して、適切な状態にしましょう。これについては以前のChapterで詳しく解説しています(Chapter25を参照)。イメージコントロールの各種調整までできたら、③の手順も完了です。


■ゲーム画面の設計

最初に載せた手順のうち、①~③までが完了しました。
ここで、いったんユーザーフォームの設定も見直しておきましょう。

普通、ゲーム画面の大きさ(ユーザーフォームの大きさ)というのは、プログラマーが始めに決めておきます。これにはちゃんとした理由があります。

実際にコードを書き始めればわかりますが、ユーザーフォームの大きさが決まっていないと、非常に困った問題が出てきます。
例えばテレビを思い浮かべてみてください。
テレビは画面の横幅と縦幅が、どんなメーカーでもおおよそ同じ規格で統一されています。なぜならそうしておかないと、表示される映像が欠けてしまったり、逆に何も表示されない空間ができてしまうからですね。縦長のテレビ画面なんて、見たことありませんもんね。

これと同様に、ゲーム画面となるユーザーフォームは、あらかじめ大きさを決めておいたほうがいいのです。もしキチンと大きさを決めておかずに、例えばブロック崩しのようなゲームを作るとしたらどうでしょうか。
ボールが跳ね返る壁が、近すぎたり、はたまた遠すぎたり、これではゲームがめちゃくちゃになってしまいますよね。

今回の場合も、あらかじめ画面のサイズ、つまりフォームのサイズを先に決めておくことにしましょう。
まずはフォームの横の幅からです。ユーザーフォームの『Widthプロパティ』に、横幅を表す数値を指定します。単位はポイントになります。
今回はここで『200』を設定しておきましょう。これで横幅が定義できました。簡単ですね。続いて縦幅も決めておきます。『Heightプロパティ』に、今回は『300』を設定しておきましょう。

それと、もうひとつ。キャラクターの初期位置も定義しておきます。つまり、先ほど画像をロードしたイメージコントロールの、表示されている位置を決めておくということです。
これには、イメージコントロールの『Topプロパティ』で縦位置を。『Leftプロパティ』で横位置を設定します。
今回はいずれも0(ゼロ)に設定しておき、次のような感じにしておきます。

171.gif

さて、ここまではうまくいっていますか?

ちょっと駆け足ですが、わからないまま進めないように注意してくださいね。ひとつひとつ、ゆっくりで構いませんから、以前の講座を参考に準備をしてみてください。

準備ができたら、早速ですが次にいってみましょう。
続いては手順の④です。

手順④では、ユーザーフォーム上にコマンドボタンを配置します。以前のじゃんけんゲームのときと同様、ここで配置するコマンドボタンは、コード実行の開始を告げるスターターの役割を果たします。
このボタンがクリックされたときに、キャラクターの移動処理が開始されるようにするわけです。こうすることによって、プログラムが動き出すタイミングをしっかり決めておくことができます。ファミコンで言うところの『スタートボタン』みたいな役割ですね。

キャラクターの移動の邪魔にならないように、今回は右下のほうに配置しました。

172.gif

これで④までが完了しました。次はいよいよキャラクターを移動させるためのコードを書いていきます。
とりあえず、実際にコードを記述する前に、キャラクターをまっすぐ動かすための考え方からまとめてみましょう。


■動くということを考える

今回は、キャラクターをまっすぐ動かすのが目的です。非常に単純な動きですね。では、一体どのようにプログラミングをすればいいのでしょう。

キャラクターを動かすということは、テレビアニメの考え方と似ています。
テレビアニメでは、セル画と呼ばれる絵を連続して表示させ、それが少しずつ変化することで、あたかも動いているように見えます。この仕組みがわからないという人はいないと思います。多分……。
プログラミングでも、この考え方と同じように、『少しずつ変化させる』というところが重要になります。

例えば地点Aから地点Bへ、キャラクターを動かしたいとします。
このとき、一瞬で地点Aから地点Bへ移動してしまったら、動いているというより瞬間移動したみたいになってしまいますね。滑らかにキャラクターが動いているように見せるためには、キャラクターを少しずつ動かすことが大切です。
テレビアニメの場合でも、例えば2枚のセル画で作られた映像より、10枚のセル画で作られた映像のほうが滑らかに動いて見えます。同じ動作であっても、その途中経過を詳細に分割して描いたほうが、より美しく動作しているように見えるのです。

そこで、繰り返し処理を使って、1回ループするたびに少しずつ動くようにプログラミングします。そうすることで、キャラクターが滑らかに動いているように見えるわけですね。

動かすということの考え方を理解できたところで、今回は、とりあえずここまでにします。
ちょっと長くなってしまったので、コードは次回紹介することにします。

キャラクターを動かすために、その考え方を理解しておくことは大切です。今回の内容をしっかり頭に入れておきましょうね。


■格言

ゲーム画面の設定をあらかじめしっかりやっておく
キャラクターの動きはテレビアニメの要領で行う


画面の設定はとても大切。始めにしっかり方向性を決めておきましょう。






Chapter.35 [ 移動処理その2:DoEvents ]

■おさらい

今回は、前回の続きです。
前回はキャラクターの移動処理について、その考え方を解説しましたね。テレビアニメの要領で、少しずつキャラクターを移動することが大切だと、解説しました。具体的には、繰り返し処理を使って、1回ループするごとに少しずつキャラクターを動かしていきます。

以前紹介した、『同期処理を取り入れたメインループ』を覚えているでしょうか。これから先、全てこのコードを主軸にすえてコードを記述していきます。これをしっかり理解していないとこれから先に進むのは難しいですので、わからない人は先にこれをしっかり理解しておいてくださいね。これについては以前の講座で詳しく解説しています(Chapter32Chapter33を参照)。

まずは先述の『同期処理を取り入れたメインループ』を見てみましょう。

Sub Main()
    Dim Flg As Boolean
    Dim Stm As Long
    Flg = False
    Do Until Flg
        Stm = GetTickCount
        'ゲームの処理
        Do
            Call Sleep(1)
        Loop Until GetTickCount - Stm > 1回のループ時間
    Loop
End Sub

このコードのポイントをおさらいしておきましょう。

このコードでは、最初に『Flg』というBoolean型の変数を宣言していますね。この変数はループの終了を定義する重要な変数です。メインループが始まる前に、まずこの変数にFalseを代入しています。そして、そのあと登場するDo文がメインループになるわけですが、そのメインループの終了条件が『FlgがTrueになったとき』となっています。
つまり、繰り返し処理を終了したいときには、変数FlgTrueを代入すればいいのですね。

それからもうひとつ。『1回のループ時間』となっている部分。ここに1回あたりのループ時間を定義します。時間の単位がミリ秒単位となっている点に注意しましょう。
ミリ秒とは1000分の1秒単位を表します。ですから1秒は1000ミリ秒です。5秒なら5000ミリ秒ですね。
1回のループ時間を1秒単位にしたいときには、この『1回のループ時間』に『1000』を指定します。0.5秒単位でループしたいなら『500』です。間違えないようにしましょうね。

それでは実際にキャラクターの移動処理を記述していくことにしましょう。
今回は1回ループするごとに、キャラクターを1ポイントずつ、下に向かって移動する場合を考えてみます。


■繰り返し処理で移動する

今回キャラクターの役割を果たすのは、前回ゲーム画面の左上に配置した『イメージコントロール』です。

180.gif

このイメージコントロールは、特に名前などを変更していないので、コードで記述するときは『Image1』という名前で指定します。これはキチンと覚えておいてくださいね。

そして、先ほども書いたように、今回はキャラクターを1ポイントずつ下に向かって移動させます。下に向かって移動するということは、縦方向の移動になります。縦方向の位置情報を管理しているのは『Topプロパティ』でしたね。

つまり、1回ループするたびに、Image1というイメージコントロールの、Topプロパティを少しずつ変化させればいいわけです。
1回のループで移動する距離を大きくすれば、一度に長い距離を動くので、見た目はすばやく移動しているように見えます。反対に、1回のループで移動する距離を小さくすれば、見た目はゆっくり移動しているように見えますね。
今回は、1回のループで、1ポイントずつ移動するようにします。このことををコードで表すと次のようになります。

Image1.Top = Image1.Top + 1

このように、Image1Topプロパティに、1を加えて新しく設定し直します。こうすることで、1回ループするたびに、毎回Topプロパティがプラス1ずつ増えていくというわけです。

ですが、このままでは、プログラムが永遠に止まりません。たとえ画面上からイメージコントロールが見えなくなってしまっても、このままだと計算上はずっと下の方向に動いていってしまいます。
そこで、どういうときに終了するのかを定義しておきます。

今回は、イメージコントロールの縦位置、つまりImage1Topプロパティが、250を超えるところまで移動してみることにしましょう。

先ほども書いたように、メインループが終了する条件は、変数FlgTrueを代入することです。ですから、Image1Topプロパティが250を超えたときに、変数FlgTrueを代入すればいいのです。これをコードで表すと次のようになります。

If Image1.Top > 250 Then
    Flg = True
End If

If文を使って、縦位置が250を超えているかどうかを毎回チェックします。もし、縦位置が250を超えていた場合には、Do文の終了条件を満たすように設定すればいいわけです。
これで、イメージコントロールの縦位置が、250ポイントより大きくなった時点で、メインループが終了し、結果的に処理が止まるようになります。

さて、とりあえずこれで移動に関する処理はOKです。
ただし、もうひとつ、とても大切なことが残っています。これをキチンとやっておかないと、原因不明の不具合に悩まされることになります。実際、私はよくこれでハマって何時間も悩んだりしました。


■DoEvents様登場

VBAのコードが実行されているときというのは、原則としてプログラムがパソコンの処理を占有しています。
わかりやすく言うと、画面の更新など、通常なら意識しなくても行われているようなパソコンの根本的な処理が、行われないということです。これはどういうことなのでしょうか。

例えば、普段パソコンを使っているとき、ウィンドウのタイトルバーをドラッグしながら動かすと、自由にウィンドウの場所を移動できますよね。フォルダをたくさん同時に開いてしまったときなどは、この機能が非常に便利です。
人間から見るとこの処理は、ただウィンドウが動いているだけです。ですがパソコンから見ると少し違います。
パソコンはこのウィンドウの移動という操作が行われると、いったん今表示されている画面を消してから、移動先に全く同じレイアウトの新しい画面を再表示しています。ユーザーがウィンドウを移動している間ずっと、消して描いて、消して描いて、を繰り返しているのです。

パソコンの根本的な処理とは、例えばこのような処理を言います。そして、VBAのコードが実行されているときというのは、これらの処理が行われなくなってしまうのです。これは困った問題です。

そこで、この問題を回避する方法として『DoEvents』という関数がVBAにはキチンと用意されています。
DoEvents関数は、呼び出された瞬間に、オペレーティングシステム(根本的な処理を行う部分)に処理を明け渡します。こうすることによって、ウィンドウの再描画などが正常に行われるようになります。

メインループの中でキチンとDoEvents関数を呼び出すようにしていないと、画面の更新などが正常に行われません。このため、せっかくキャラクターを移動するコードを実行させているのに、画面上の見た目が全く変わらなくなってしまいます。ずーっとキャラクターは最初の位置から動かないわけです。
内部的にはキチンと計算などが行われ、設定が変化しています。しかし見た目が変わらないとあっては意味がありません。ループするたびに、きちんとDoEvents関数を呼び出すようにしておかなくてはいけません。

コードの記述はどこも間違っていないのに、画面上でキャラクターが移動してくれないときなどは、このDoEvents関数の呼び出しを忘れてしまっていることが多いです。画面の更新が行われないわけですから、移動してくれないのは当たり前です。毎回少しずつ移動させるわけですから、DoEvents関数を毎回呼び出すように設定しておきましょう。

これは非常に大切なことです。ついつい忘れてしまいがちなことですから、しっかり覚えておいてくださいね。


■コードの全容

さて、ちょっと前置きが長くなりましたが、メインループに移動処理を加えた、完成版コードを載せます。このコードを、『標準モジュール』に記述しましょう。新しく標準モジュールを準備するときは、メニューの『挿入』から『標準モジュール』を選択すればOKです。これで空の標準モジュールができるはずですから、そこに下のコードを記述すればいいわけです。

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)
Declare Function GetTickCount Lib "kernel32.dll" () As Long

Sub Main()
    Dim Flg As Boolean
    Dim Stm As Long
    Flg = False
    Do Until Flg
        Stm = GetTickCount
        With UserForm1.Image1 '①
            .Top = .Top + 1 '②
            If .Top > 250 Then '③
                MsgBox "移動完了"
                Flg = True '④
            End If
        End With
        DoEvents '⑤
        Do
            Call Sleep(1)
        Loop Until GetTickCount - Stm > 50 '⑥
    Loop
End Sub

ちょっと長いですので大変ですが、ゆっくりひとつひとつ見ていきましょう。

メインループの中で、Withステートメントを使っている部分がありますね。①の部分です。そこには『With UserForm1.Image1』と記述されています。
以前、Withステートメントについては解説しましたね(Chapter23を参照)。『End With』という記述があるまでは、ピリオドに続けてプロパティを記述するだけで同様の設定を行うことができます。これにより無駄を省き、簡略化することができるのでしたね。

そして②のところで、Image1Topプロパティを、1回ループするたびに1ポイントずつ増やしていきます。これにより、イメージコントロールの縦位置が、毎回少しずつ下に移動していきます。

③の部分では、イメージコントロールの現在の位置が、250を超えていないかどうかチェックしています。もしもイメージコントロールの縦位置が、250よりも大きなっていた場合には、④で示すように、メインループの終了条件である『変数FlgへのTrueの代入』を行います。これで、メインループが終了し、結果的にコードが止まるわけです。

忘れてはいけないのが⑤の部分。メインループの間は毎回キチンとDoEvents関数を呼び出すようにしておきましょう。これがないとキャラクターは移動しているように見えませんからね。

⑥では、1回のループをどれくらいの早さで行うかを設定しています。今回はここで50ミリ秒を設定しています。50ミリ秒で1回ループするということは、1秒が1000ミリ秒ですから、1秒間におよそ20回ループすることになります。

どうですか? コードの内容はキチンと理解できたでしょうか。


■仕上げ

さて、最後の仕上げです。
先ほどのコードで、移動処理は完璧です。あとは、ユーザーフォームに設置したコマンドボタンから、このコードが呼び出されるようにすればいいのですね。これはじゃんけんゲームで使ったのと同じ手法です。

コマンドボタンのクリックイベントに、次のように記述しましょう。

Private Sub CommandButton1_Click()
    Call Main
End Sub

これで、コマンドボタンをクリックしたときに、先ほどのコードが呼び出されるようになりました。フォームを実行して表示させコマンドボタンをクリックすれば、赤い機体が下の方向に移動していくはずです。一定の場所に達すると、メッセージが表示され、処理が終了します。

さて、うまくいったでしょうか。

前回と今回の2回で、非常に単純な移動の処理について解説してきました。下に向かって移動するだけという単純な処理でも、結構考えなくてはならないことがたくさんありましたね。
ちょっと面倒な気がしたかもしれませんが、どのような複雑な移動処理であっても、考え方は同じです。今回の内容をしっかり理解していれば、これから先の講座でも、すんなり理解することができるでしょう。

途中、DoEvents関数の呼び出しなど、とても大切な仕組みが出てきました。焦らず、ひとつひとつ確実に覚えていってください。


■格言

1回のループでどのくらい移動するか定義する
どのタイミングで終了するかキチンと定義する
DoEvents関数をキチンと呼び出す


覚えることがたくさんあって大変です。






実践講座1:動きのあるゲームをつくる

長い長い基礎講座を完了し、めでたく実践講座がスタートしました。

以前から何度も言っているように、基本を焦らずしっかり覚えることは非常に大切です。なかなか具体的なゲームの話が出てこなくて、ちょっと物足りなかったかもしれませんが、基本がわかっていないうちに、いきなり具体的な話はできません。
具体的な話をしようとすれば、当然、難しい概念や、普段あまり耳にすることのないような単語を使う場面が出てきます。そんなときに、スムーズに理解するためにも、基礎をしっかり解説したかったのです。

さて、実践編の第一部。
ここではいよいよ具体的なゲームコードを紹介していくことになります。
まずは、動きのあるゲームを作るために必要なテクニックを中心に、ゲームを作る際に役立つ情報を解説できたらいいなと思っています。

もし、少しでも引っかかる部分があったときは、いったん過去のChapterに戻って、復習することも大切です。わけがわからないまま、サンプルコードをコピーしているだけでは、一向に上達しません。

わからないことは決して恥ずかしくありません。わからなければ調べればいいだけのことです。
自分で書いたコードが、意図しない結果を招いたとしても、それは無駄なことではありません。失敗した分だけ、きっとあなたは成長できます。

とにかく大事なことは、『諦めないこと』です。そして『わかることから地道にマスターしていくこと』も大事です。
焦らず、自分のペースでプログラミングしていきましょう。


    Chapter.34 [ 移動処理その1:画面設定と考え方 ]

    Chapter.35 [ 移動処理その2:DoEvents ]

    Chapter.36 [ 移動処理その3:キー入力判定API ]

    Chapter.37 [ 条件分岐のさらなる探求 Select Case ]

    Chapter.38 [ アニメーション ]

    Chapter.39 [ 配列変数 ]

    Chapter.40 [ ゲームの初期化 ]

    Chapter.41 [ シューティングゲーム1:ゲーム設計 ]

    Chapter.42 [ シューティングゲーム2:メインプロセス ]

    Chapter.43 [ シューティングゲーム3:構造体 ]

    Chapter.44 [ シューティングゲーム4:定数 ]

    Chapter.45 [ シューティングゲーム5:プレイヤーキャラクター ]

    Chapter.46 [ シューティングゲーム6:ショットを撃つ① ]

    Chapter.47 [ シューティングゲーム7:ショットを撃つ② ]

    Chapter.48 [ シューティングゲーム8:Mod演算子の活用 ]

    Chapter.49 [ シューティングゲーム9:敵キャラクター登場 ]

    Chapter.50 [ シューティングゲーム10:衝突判定 ]

    Chapter.51 [ シューティングゲーム11:衝突の実体 ]

    Chapter.52 [ シューティングゲーム12:敵の攻撃 ]








Chapter.36 [ 移動処理その3:キー入力判定API ]

■自由度の高い移動処理

前回はかなり内容が複雑でしたが、皆さん、無事にマスターできたでしょうか。

さて、移動処理の3回目となる今回は、『キー入力の判定』について解説したいと思います。

前回の講座で、キャラクターを移動させる方法を紹介しました。繰り返し処理を使って、キャラクターに見立てたイメージコントロールを、少しずつ動かして移動させましたね。まずは入門編という意味で、前回は直進するだけの移動を解説しました。

しかしゲームの種類にもよるんでしょうが、シューティングゲームやアクションゲームで考えた場合、大抵は主人公となるキャラクターが、プレイヤーの意思に応じて自由に動かせるようになっています。
前回のように、直進するだけの移動では、これがうまく実現できませんね。まっすぐにしか進めないルールのゲームならともかく、普通はプレイヤーが好きなようにキャラクターを操作できるようになっているはずです。

今回は、この『プレイヤーが自由に操作できるキャラクター』を実現するべく、『キー入力の判定』というテーマを掲げました。今回の講座の内容をしっかりと習得できれば、キーボードのカーソルキーを使って、自由に移動するキャラクターを作ることができます。これで作成できるゲームの幅が大きく広がりますので、是非、がんばって習得してほしいと思います。


■キー入力判定の種類

実は、VBAでキーの入力を判定する方法は、ざっと分けて2種類あります。

1つ目は、『キー入力イベントを使用する』という方法です。
VBAは非常にイベントと相性のいい言語です。……と言ってもよくわからないですよね。相性がいいってどういうこと? そもそもイベントってなによ? っていう感じだと思います。

実はじゃんけんゲームのところでも少し触れたのですが、イベントの概念を理解していることは、ゲームを作成する上で非常に重要です。この機会にしっかり覚えておいてください。

イベントというのは、パソコンを使用するユーザーの操作を言います。
例えば、『コマンドボタンを押す』というユーザーの操作は、パソコン側から見ると『コマンドボタンのクリックイベント』です。
同様に、『Excelのファイルを開く』というユーザーの操作は、パソコンから見れば『Excelファイルのオープンイベント』になります。
画面上でマウスが動いているだけだって、パソコンから見ると『マウスのムーブ(Move)イベント』なんです。少しイメージしにくいかもしれませんが、我々ユーザーがパソコンで何らかのを操作すると、必ず、何かしらの反応がありますよね。
キーを押したり、クリックしたり、そういった操作をすることで、必ず何かしらの成果があります。これは、パソコン側がユーザーの操作に対して反応してくれるおかげです。

パソコンは、このように、ユーザーの操作の全てを『イベント』として受け取ります。そして、それに応じた処理を行ってくれるのです。

VBAは、この『イベント』を用いてプログラミングがしやすい仕組みになっています。実際に今までも、コマンドボタンのクリックイベントを何度か講座内で使っていますよね。

キー入力判定の1つ目の方法は、このイベントを使ってキー入力判定を実装する方法です。細かい仕組みは今回解説しませんが、タイピングゲームや、単純なキー入力だけで作成できるパズルゲームやカードゲームなど、それほど激しい判定を必要としないゲームを作成する場合には、非常に役に立つ手法のひとつです。


さて、それに対して、キー入力判定のもうひとつの方法が、『APIを使ってキー入力を判定する』という方法です。

この方法は、先ほどのイベントでのキー入力判定に比べ、非常に細かく、激しい入力を必要とするゲームに向いています。シューティングやアクションを作成したいと考えた場合には、ほぼ必須のテクニックといってもいいでしょう。
APIを使用しているので、一見とっつきにくい印象を持ちますが、私の感覚では、イベントでのキー入力判定よりも簡単なような気がします。

今回は、この『APIを使ってキー入力を判定する』方法について、具体的に見ていきたいと思います。


■キー入力判定APIの仕組みを理解する

まずはキー入力判定に使用するAPIから紹介します。
そのAPIは『GetAsyncKeyState』という、少し名前の長いAPIです。

GetAsyncKeyState関数は、今現在キーが押されているかどうかを、リアルタイムに教えてくれる関数です。多少の予備知識が必要ですが、使い方は非常に簡単なので、この機会に覚えておくといいでしょう。

Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Long

宣言文はこのようになっています。

注目すべきは、括弧で囲まれている部分です。そこには『(ByVal vKey As Long)』と書かれていますね。そこに『~ As Long』とあることから、この関数が引数として整数型のデータを取ることがわかりますね。ここに、各種のキーを表すコードを入れて関数を呼び出すと、そのキーが今現在押されているのかどうか、結果を教えてくれます。
具体的に言うと、次の例で示すように、任意のキーを表すコードを渡してGetAsyncKeyState関数を呼び出します。すると、GetAsyncKeyState関数は何らかの数値を返してくるので、その結果を変数に確保します。
変数の中に入っている数値が、0(ゼロ)よりも小さい数値だった場合には、そのキーが押されているということになり、それ以外の場合には、キーが押されていないということになります。

※ここで登場する変数 L はLong型の変数です。

L = GetAsyncKeyState(調べたいキーのキーコード)

ここで変数 L の中身を調べるとキー入力の状態がわかる。
0よりも小さい数値の場合(L < 0なら)キーは押されている。
それ以外の場合にはキーは押されていない。

各キーがどのようなコードで表されるのかは、この講座の最後に、一覧表で用意してあります。使い方が理解できたら、活用してみてください。


それでは、実際にキャラクターを移動する処理に、このAPIを取り入れて考えてみましょう。

前回のコードを改造して、キーの入力を受け付けるようにしてみます。
変更するのは以下の点です。

①キーボードのカーソルキーごとに状態を調べる
②押されているキーがあるなら移動するようにする
③コードの終了を定義するためにひと手間加える

まずは①です。

キーボードのカーソルキーは、それぞれ次のようなキーコードで表されます。
 ←キー(左):37
 ↑キー(上):38
 →キー(右):39
 ↓キー(下):40

カーソルキーは、ご存知の通り4つの方向を持っています。ですから、4つのキーの状態をGetAsyncKeyState関数でそれぞれ調べます。GetAsyncKeyState関数の引数に、上のキーコード(37~40)を入れて呼び出せばいいのですね。

そして次は②の部分です。
先ほど調べたGetAsyncKeyState関数の結果から、もしキーが入力されている(押されている)と判断できた場合には、それぞれの方向に、TopプロパティやLeftプロパティを適宜設定し直して、表示される位置を変更します。

こうすることで、1回のループが行われるたびに、キーの状態に応じてキャラクターが動いているように見えるようになります。

しかし、重要なのが③のところです。
前回は、移動の仕方が真下方向への直進だけでしたね。ですから、ある特定の場所に来たときに、強制的に繰り返し処理を終了して、コードを止めました。
今回の場合は、キー入力の状態によって、どこにキャラクターが表示されているのかは不確定です。特定の場所に来たときに処理が終了するという現状のやり方は、コードを止めるための判断とはなりにくいですね。

そこで、ユーザーフォームが閉じられたときには、全てのコードが止まるようにしておきましょう。
こうしておけば、ユーザーフォームの『×』ボタンがクリックされると同時に、強制的に処理が止まるようになり、確実にコードを止めることができます。
これを実現するためには、ユーザーフォームの『QueryCloseイベント』を使います。


■QueryCloseイベント

QueryCloseイベントとは、『ユーザーフォームが閉じられる直前』に発生するイベントです。
ユーザーフォームの右上にある、『×』ボタンが押された瞬間に発生します。

このとき、コードを強制的に止めてしまえば、繰り返し処理が確実に停止するので安全です。
注意しなくてはならないのは、コードを記述する場所です。
QueryCloseイベントはユーザーフォームで発生するイベントなので、標準モジュールに記述してはいけません。絶対にフォームモジュールに記述してくださいね。具体的には、フォームモジュールに以下のコードを記述します。

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    End
End Sub

なんとあっけないコードなんでしょうか……。実際に必要なのは『End』というところだけです。これはEndステートメントと呼ばれており、『End Sub』や、『End If』などのときに使用するEndと同じものです。
Endと単体で記述した場合には、実行されている全てのコードが強制的に終了します。この機能を使って、繰り返し処理しているコードを強制的に止めます。

フォームモジュールに記述しなくてはならない、というルールさえ間違えなければ、大丈夫なはずです。簡単ですね。


さて、それでは実際に繰り返し処理を行う部分を見てみましょう。
GetAsyncKeyState関数を呼び出している部分と、それに応じてTopプロパティやLeftプロパティが変更されている部分。これがわかれば、あとはほとんど前回と変わりません。

Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)
Declare Function GetTickCount Lib "kernel32.dll" () As Long
Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Long
'GetAsyncKeyStateのAPI宣言文を忘れずに追加しましょう

Sub Main()
    Dim Stm As Long
    Dim Left_Key As Long
    Dim Up_Key As Long
    Dim Right_Key As Long
    Dim Down_Key As Long '各キーの状態を確保する変数を宣言
    Do
        Stm = GetTickCount
        Left_Key = GetAsyncKeyState(37)
        Up_Key = GetAsyncKeyState(38)
        Right_Key = GetAsyncKeyState(39)
        Down_Key = GetAsyncKeyState(40) '各キーの状態を取得
        With UserForm1.Image1
            If Left_Key < 0 Then '左
                .Left = .Left - 1
            End If
            If Up_Key < 0 Then    '上
                .Top = .Top - 1
            End If
            If Right_Key < 0 Then '右
                .Left = .Left + 1
            End If
            If Down_Key < 0 Then '下
                .Top = .Top + 1
            End If
        End With
        DoEvents
        Do
            Call Sleep(1)
        Loop Until GetTickCount - Stm > 50
    Loop
End Sub

新しいAPIとしてGetAsyncKeyState関数が増えています。ちゃんとAPIの宣言文を記述していないとエラーがでますので、忘れずに宣言しておきましょう。

GetAsyncKeyState関数の宣言さえキチンとできていれば、あとはGetAsyncKeyState関数でキーの状態を調べて、If文で各方向ごとに条件分岐させるだけです。これでキャラクターの移動が、キー入力の状態によって変化するようになりました。

あとは前回と同じように、コマンドボタンからこのコードが実行されるようにしてあればOKです。
ユーザーフォームのQueryCloseイベントも、忘れずに準備しておきましょう。そうしておかないと、処理が止まりません、永遠に……。

恐いです。

さて、結構長くなってしまいましたね。
わかりづらい部分もあるかもしれませんが、ひとつひとつ、確実にこなしていってください。気をつけなくてはならない部分が多かったですが、内容としては単純なものがほとんどです。焦らずひとつひとつ設定していけば、前回のコードを流用して意外と簡単に実装できるはずですから、がんばってみてください。

これで、なんとなくシューティングゲームっぽい感じになりましたね。
キーの入力を判定できるといろいろなことができるようになります。ゲームの根本部分に関わるところなので、結構重要な部分です。マスターすればかなりの実力アップになりますよ。


■格言

キー入力判定には2種類の方法がある
GetAsyncKeyState関数の使い方を理解する


以下にキーコードの一覧を載せておきます。必要に応じて、調べてみてください。


キーの種類キーコードキーの種類キーコード
BackSpace
8
A
65
Tab
9
B
66
Enter
13
C
67
Shift
16
D
68
Ctrl
17
E
69
Alt
18
F
70
Pause
19
G
71
Esc
27
H
72
Space
31
I
73
PageUp
33
J
74
PageDown
34
K
75
End
35
L
76
Home
36
M
77
37
N
78
38
O
79
39
P
80
40
Q
81
Insert
45
R
82
Delete
46
S
83
*
106
T
84
+
107
U
85
-
109
V
86
.
110
W
87
/
111
X
88
0(キーボード)
48
Y
89
1(キーボード)
49
Z
90
2(キーボード)
50
F1
112
3(キーボード)
51
F2
113
4(キーボード)
52
F3
114
5(キーボード)
53
F4
115
6(キーボード)
54
F5
116
7(キーボード)
55
F6
117
8(キーボード)
56
F7
118
9(キーボード)
57
F8
119
0(テンキー)
96
F9
120
1(テンキー)
97
F10
121
2(テンキー)
98
F11
122
3(テンキー)
99
F12
123
4(テンキー)
100
NumLock
144
5(テンキー)
101
CapsLock
240
6(テンキー)
102
半/全
244
7(テンキー)
103


8(テンキー)
104


9(テンキー)
105









Chapter.37 [ 条件分岐のさらなる探求 Select Case ]

■もうひとつの条件分岐

キー入力の判定について解説した前回。ちょっと難しい内容でしたが皆さんうまくできたでしょうか。

キーの入力を拾うことができれば、ユーザーに様々な操作を要求することができます。必ずしも複雑な操作が優れているわけではありませんが、ゲームの『表現力』をアップさせることができるのは間違いありません。
プログラミングをする我々開発者の立場から見ても、様々な演出を行うことが可能となり、作成できるゲームの幅が大きく広がります。
一度に理解できなくてもいいのです。また時間を置いて、以前の講座を見直したときに理解できるかもしれません。わからないまま進めるのではなく、ひとつひとつこなしていきましょう。

さて、今回のテーマは『もうひとつの条件分岐』となっています。

条件分岐といえば、真っ先に思い浮かべるのはIf文でしょう。
これはプログラミングに欠かせない要素で、今までにも、講座内で何度も登場しています。ある程度のことをしようと思ったら、絶対に必要になる概念です。

しかし、VBAには、もうひとつ、条件分岐の方法が用意されています。
今回は、このもうひとつの条件分岐について見ていきます。そして、なぜIf文以外にもうひとつの条件分岐が用意されているのか、その方法を使うことによってどんなメリットがあるのか。これについて解説したいと思います。


■Select Case

If文とは違う、もうひとつの条件分岐。それはSelect Case文と呼ばれています。
このSelect Case文、普段あまり使わない人にとっては、『所詮条件分岐でしょ、If文があれば十分じゃない?』とか『別にこれを使わなくっちゃできないことなんてないし』っていう程度のものかもしれません。

ところがこのSelect Case、ものすごく使えます。少なくとも私は、Select Case文なしでゲームを作ることはないでしょうね。
ではどこがどう使えるのか、それを見ていきましょう。


まずはちょっと思い出してみてください。以前の講座でも解説しましたが、If文の場合『正しいとき』と、『正しくないとき』という判断の仕方をしています。このため、If文は基本的には二者択一の考え方で記述されます。

If文の場合

    If 条件 Then
        条件が(正しい)のとき
    Else
        条件が(正しくない)のとき
    End If

これに対して、同じ条件分岐処理の方法であるSelect Case文は、全く違った考え方をします。
最も簡単に記述すると、Select Case文は次のようになります。

Select Case文の場合

Select Case 式、または値
    Case 結果A
        結果Aの場合の処理
    Case 結果B
        結果Bの場合の処理
    Case 結果C
        結果Cの場合の処理

        :
        : ※Case ~ は、いくつでも指定可能
        :

    Case Else
        結果が上記のどれにもあてはまらない場合の処理
End Select

基本的に二者択一の処理を行うIf文に対して、Select Case文は3つ以上の選択肢に対応することが簡単です。
『式、または値』の内容が、Aの場合、Bの場合、Cの場合…………と、いろいろなケースに応じて、好きなように処理を分岐することができます。

Select Case文の場合には、まず、基本となる式か、あるいは値を設定します。そして、その状態を調べた結果に応じて、そのあとの処理が分岐します。
Select Case文の最後は、If文のときなどと同様に、Endを使って、『End Select』という形で終わります。
ちょっとわかりづらいですね。簡単な例を挙げて考えてみましょう。

私は今、商店街でとある行列に並んでいる。目的は地元の商店街組合が主催する福引だ。
今回の福引、なんと景品が海外旅行ペアチケット! 財布の中には福引券がたくさん入っている。ふふふ、日ごろから商店街で買い物してきた甲斐があった。
ちなみに、当たりを引いた場合には『アメリカ・フランス・オーストラリア・ロシア・中国』の5つのうち、どれかひとつがもらえるらしい。
捕らぬ狸の皮算用なのはわかっているが、つい当たったときのことを考えて頬が緩んでしまう。

…………こんな人いますか? っていう感じもしますが、まぁ例えですから。大目に見てください。

さて、とにもかくにも、上の話をSelect Case文に当てはめるとどのようになるでしょうか。ちょっと無理矢理ですがコードにしてみます。

Select Case 福引の抽選結果

    Case アメリカ旅行ゲット!
        アメリカへ旅立つ身支度を始める

    Case フランス旅行ゲット!
        フランスの旅ガイドを買う

    Case オーストラリア旅行ゲット!
        コアラの生態を調べる

    Case ロシア旅行ゲット!
        寒さ対策を考える

    Case 中国旅行ゲット!
        中国語を勉強する

    Case Else つまりハズレ
        ハズレの景品、ティッシュで鼻をかむ

End Select

なんじゃこりゃ……。

ま、まぁ細かいことは置いといて、あまり深く突っ込まないでください。

『式、または値』に相当するのは『福引の抽選結果』です。そして、それ以降、『福引の抽選結果』の内容に応じて、それぞれ処理が分岐しているのがわかりますね。
もし何らかのチケットが当たった場合には、最初の5つのうち、適合する場合の部分に処理が進みます。
どれにもあてはまらなかった場合、つまりハズレの場合には、最後の部分に処理が進みます。

このように、Select Case文では、複数の結果から判断して処理を分岐することができます。これはIf文を使って、3択以上の条件分岐を記述するのに比べると、格段に簡単でわかりやすい記述方法ではないでしょうか。

ためしに、上のような状態をIf文で表してみると、その差がわかるのではないでしょうか。

If 抽選結果 = アメリカ Then
    アメリカへ旅立つ身支度を始める
Else
    If 抽選結果 = フランス Then
        フランスの旅ガイドを買う
    Else
        If 抽選結果 = オーストラリア Then
            コアラの生態を調べる
        Else
            If 抽選結果 = ロシア Then
                寒さ対策を考える
            Else
                If 抽選結果 = 中国 Then
                    中国語を勉強する
                Else
                    ハズレの景品、ティッシュで鼻をかむ
                End If
            End If
        End If
    End If
End If

見づらい上に、ちょっとわかりづらいですよね。これならSelect Case文を使ったほうが見た目もスマートですし、わかりやすいです。
条件分岐にIf文を使うのか、それともSelect Case文を使うのかは、ハッキリ言ってケースバイケースです。それに、好みの問題もあるでしょう。
ただ、多くの可能性を含んでいる条件分岐では、Select Case文を使ったほうがコードが簡潔に美しく記述できます。
自分が見てわかりやすいということを第一に、状況に応じてうまく使い分けていきましょう。


■さらなる条件の高みへ

さて、基本的なSelect Case文の使い方は理解できたでしょうか。なんだか今更ですが、今回のようなおちゃらけた講座内容でいいのか、という気もしてきました。わかりづらかったらすいません。

ここから先は、Select Case文の条件指定について、少しだけ発展した方法を解説します。

Select Case文の記述の仕方には、実は様々な方法があります。
これが使いこなせると実に便利です。余裕のある方は是非習得してください。

Sub SelectCase_Test()
    Dim Counter As Long
    For Counter = 1 To 10
        Select Case Counter
            Case 1
                MsgBox "変数の値は1" '①
            Case 2, 4, 6
                MsgBox "変数の値は2か4、または6" '②
            Case 3 To 7
                MsgBox "変数の値は3から7までの間" '③
            Case Is < 9
                MsgBox "変数の値は9より小さい" '④
            Case Else
                MsgBox "変数の値は9か10" '⑤
        End Select
    Next
End Sub

For文で、1~10までの繰り返し処理をしながら、毎回メッセージを表示しているだけの単純なコードです。
しかし、Select Case文での指定の仕方は①~⑤で全て異なっています。

それでは①から順番に見ていくことにしましょう。

まず①は、もっとも単純なSelect Case文での条件指定です。変数Counterの値が『 1 』だった場合にはここに進みます。単体での指定ですね。

②は、カンマ(コレ→ , )で区切って指定していますね。
このように指定することで、複数の結果に対して、同様の設定を一括で行うことができます。

次は③です。ここでは『To』というキーワードを使って、特定の範囲をまとめて指定しています。数値でこの方法を使った場合には、左辺から右辺までの間の、全ての数値を指定したことになります。
適用範囲が広い場合などには有効に使えますね。

④では『Is』というキーワードが出てきます。
ここで出てくるIsは、この場合『変数Counterそのもの』を指しています。ですから『Counter < 9』という式と同じ意味になります。変数Counterの値が9より小さい場合に適用されます。

最後は⑤です。これは上で指定した結果のどれにもあてはまらなかった場合です。先ほども出てきましたので簡単ですね。上の①~④までの条件全てにあてはまらなかったということは、変数Counterの値は9か10ということになります。


①と、⑤は普通によく使いますが、意外と②~④の指定方法は知らない方もいるのではないでしょうか。
これらの方法を全て駆使すると、かなり複雑な条件分岐を行うことができます。当然、コードの柔軟性がかなりアップしますので、いろいろなことができるようになります。

また、実際に上のコードを実行させてみればわかると思いますが、Select Case文は『最初に適合した結果のみを実行する』ということを覚えておきましょう。

たとえば先ほどのコードで、変数Counterに『 4 』が設定されているときを考えてみてください。②~④のどれにも適合してしまっていますよね。

変数Counterの値が4だった場合

②の条件は『2か4か6』なので適合している。
③の条件は『3から7の間』なので適合している。
④の条件は『9より小さい数』なのでやっぱり適合している。

こんなとき、Select Case文は最初に適合した場合の処理のみを行うので、実際には『変数の値は2か4、または6』というメッセージが表示されるだけで処理が完了します。

これは実に重要な仕組みです。

このことを正しく理解せず、適当にたくさんの選択肢を設定しないように気をつけましょう。私はたまにコレではまります。


■格言

条件分岐にはIf文のほかにSelect Case文がある
たくさんの条件を指定するときには非常に便利
条件の設定方法も実に多彩


使いこなせれば、確実にレベルアップです。がんばって習得してください。






Chapter.38 [ アニメーション ]

■さらなる表現を目指して

さて今回は、更なる表現力を身につけるために、キャラクターのアニメーションについて解説したいと思います。

キャラクターをアニメーションして動かすというのは、どんなゲームを作成する場合にも非常に有効な演出となります。ただ単に静止画としての画像を表示するのもいいですが、やはり、アニメーションされている画像が表示されていたほうが、見た目のかっこよさは格段に上です。

例えば顔絵ひとつしか表示されない卓上ゲームのようなものだって、キャラクターが表情を様々に変えれば、それはそれでかなりの演出効果が見込めます。ビリヤードなどではボールが勢いよく転がりながら動き、徐々にスピードを落としていく演出などで、やはりアニメーションが有効です。これにより、リアリティが増すからですね。

今回の内容は、その性質上コーディング以外の準備が必要になります。それがまた結構手間なんですね。
しかし、手間をかけることさえ惜しまなければ、そのかかった手間以上にすばらしい演出をすることができます。サンプルを掲示しながら進めていきますので、諦めずにがんばりましょう。

というわけで、アニメーション処理、是非ものにしてください。


■仕組みから考える

先ほども言いましたが、アニメーション処理を実現するためには、それなりに事前の準備が必要です。

まず、最低限必要なものは、テレビアニメでいうセル画の役割を果たす、一連の画像です。大抵の場合、少しずつ見た目の異なる複数の画像ですね。
先ほど手間がかかると言ったのは、実はこの画像を準備するところのことなんです。

普段からパソコンでよく絵を描いているという人なら、それほど大変ではないのかもしれませんが、そのような作業に慣れていない人が、アニメーションの原画を描くというのは容易なことではありません。
ウィンドウズに付属されている『ペイント』などの画像編集ソフトで、地道に画像を準備するのは結構大変なんですね。写真を使うとしても、アニメーションしているように撮るというのは結構手間です。第一難しそうですよね。

ただ、悲しいことに、コレばっかりは自分でどうにかするしかありません。インターネット上で探したり、奮起して自分で用意したり、方法は何でもいいです。とにかく、アニメーションするキャラクターなどを表現するためには、まず元となる画像がなくては話になりません。

今回はサンプルとして、以前私がボツにしたキャラクターの画像を載せることにします。白状してしまうと、あらためて描くのが面倒だったからです。許してください。手抜きじゃないと言ってください。

……とにかく、下の画像が今回使用するサンプルです。

 画像1  画像2  画像3 
booty1.gif
booty2.gif
booty3.gif

画像のサイズは24×24ピクセルです


これらの画像は、キチンと透過処理されたGIF形式の画像です。これを使ってアニメーション処理を実装してみましょう。

ちなみに、基盤となるコードはいつもどおり『同期処理のとれたメインループ』です。これがよくわからないという人は、以前の講座でこの仕組みをよく理解してからチャレンジしてみてください(Chapter34Chapter35を参照)。


さて、まずはアニメーション処理の仕組みから考えてみます。

アニメーション処理は、用意された一連の画像を、ループしながら切り替えて表示することで実現させます。
1回目のループ時には『画像1』を表示させ、2回目のループ時は『画像2』を表示。3回目は『画像3』を表示して、4回目は再び最初に戻り『画像1』を表示します。あとは同様に切り替えながら繰り返し処理します。

190.gif

少しずつ見た目の異なる画像を、連続して表示すると、人はそれが動いているように見えます。ループしながら画像をうまく切り替えて、アニメーションしているように見せるのです。

ここで問題となるのは、画像をどこに置いておくのかです。

ユーザーフォーム上でキャラクターを動かしたときを思い出してください。イメージコントロールに画像をロードして、それをキャラクターに見立てていましたよね。
アニメーションを実現する場合も、これと同様に、使用する画像をイメージコントロールにあらかじめロードしておく必要があります。

今回は3つの画像を切り替えながらアニメーションするので、普通に考えると3つのイメージコントロールを用意すれば良さそうですね。
しかし、効率よく処理を行うために、今回はイメージコントロールを4つ用意します。下の画像を参考にして、まずはフォームの準備をしてみてください。

191.gif



■画像を切り替える仕組み

さて、上手にフォームを準備できたでしょうか。
各コントロールの名前などは、忘れずに設定しておいてくださいね。

まずは、表示したい画像は3つなのに、なぜ用意したイメージコントロールは4つなのか、これについて解説します。

画像をロードした3つのコントロールは、『原画』の役割を果たすイメージコントロールです。あくまでも、画像の雛形として、原画の役割を担います。
これに対し4つ目のコントロール、つまり『Ima_Chara』という名前をつけたコントロールは、キャラクターそのものとして使います。このイメージコントロールの画像を切り替えながら表示することにより、あたかもキャラクターがアニメーションしているように見せるのです。

つまり、実際にアニメーションしているように見えるのは『Ima_Chara』というイメージコントロールだけで、ほかの3つはアニメーション処理の原画として使用するだけなのです。

画像をデータとして保持しているのは『イメージコントロールのPictureプロパティ』でしたね。
ですから、イメージコントロールの画像を切り替えたいときには、Pictureプロパティに新しい画像を設定すればいいわけです。

具体的には、キャラクターそのものである『Ima_Chara』のPictureプロパティに、新しく設定したい画像をロードしてある、イメージコントロールのPictureプロパティを設定します。PictureプロパティからPictureプロパティへ、画像データの受け渡しを行うわけです。
感覚としては、イメージコントロールからイメージコントロールへ、画像を転送しているような感じですね。

画像1を表示させたい場合は……

    Ima_Chara.Picture = Image1.Picture

画像2を表示させたい場合は……

    Ima_Chara.Picture = Image2.Picture

画像3を表示させたい場合は……

    Ima_Chara.Picture = Image3.Picture

理屈がわかってしまえば簡単ですね。

それでは実際にコードを書いてみましょう。

Sub Anim_Test()
    Dim Stm As Long
    Dim Counter As Long
    Do
        Stm = GetTickCount
        Counter = Counter + 1
        If Counter > 3 Then '①
            Counter = 1
        End If
        With UserForm1
            Select Case Counter '②
                Case 1
                    .Ima_Chara.Picture = .Image1.Picture
                Case 2
                    .Ima_Chara.Picture = .Image2.Picture
                Case 3
                    .Ima_Chara.Picture = .Image3.Picture
            End Select
        End With
        DoEvents
        Do
            Call Sleep(1)
        Loop Until GetTickCount - Stm > 150 '③
    Loop
End Sub

コードの内容はそれほど難しいものではないと思います。

Long型の変数Counterが、1回ループするたびにプラス1ずつ増えます。ただしそのあと、もし変数Counterの値が3より大きい数(4以上の数)だった場合には、1に戻しています。これが①の部分ですね。

ここで変数Counterに設定された値を使って、そのあとの処理を行います。
それが②の部分です。

Select Case文で、変数Counterの値を元に処理が分岐しています。さきほどの①の部分で、変数Counterに入る数値は1~3の範囲を繰り返すようになっているはずです。それに応じて画像が切り替えられ、1回ループするたびに毎回違った画像が表示されるのです。
忘れやすいのが『DoEvents』の呼び出しです。これは注意してくださいね。もし呼び出すのを忘れていると、画面が更新されないので一向にアニメーションしているように見えません。

今回はあまりループが早すぎると、アニメーションが速くなりすぎて不自然になります。そこで③の部分で示すように、前々回よりも1回あたりのループ時間を長めにしてあります。(前々回はここが50に設定されていました)


■仕上げ

さて、アニメーション処理のメインとなるコードは、これで準備できました。

あとは前々回の講座のときと同じ要領で、コマンドボタンからこのプロシージャを呼び出すようにしておきます。
ユーザーフォームのQueryCloseイベントも、忘れずに記述しておきましょうね。QueryCloseイベントをキチンと記述していないと、処理が止まらなくなってしまいますからね。

一応参考までに、フォームモジュールに記述するべきコードも載せておきますね。
標準モジュールではないですから、十分注意してください。

'コマンドボタンのクリックイベント
Private Sub CommandButton1_Click()
    Call Anim_Test
End Sub

'----------------------------------------------------------------------

'ユーザーフォームのクエリクロースイベント
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    End
End Sub

このコードがよくわからない人は、前々回の講座で詳しく解説していますのでそちらをご覧ください(Chapter36を参照)。

さて、これで準備は全て完了です。
ユーザーフォームを表示させ、コマンドボタンを押すと、キャラクターがアニメーションして表示されるはずです。オレンジの丸いヤツが、うんうん頷いているような感じに、アニメーションして表示されましたか?

booty4.gif
こんな感じ。

仕組みさえ理解できてしまえば、アニメーション処理はそれほど難しくはありません。
焦らずひとつひとつ設定していけば、特に大きな問題はないでしょう。

最初に言いましたが、大変なのはむしろ画像を用意しなくてはならない、ということではないでしょうか。こだわりのあるゲームほど、キャラクターがしっかり描き込まれています。
少しだけ根気を出して、自分自身が納得のいく画像を用意してみてくださいね。

ちなみに、今回のコードはもう少し効率よく記述することもできます。
しかし、内容が濃すぎになってしまいますので、これはいつか、おいおい解説することにします。アニメーション処理はゲームの中で頻繁に登場します。近いうちに解説できると思いますので、まずは今回の内容をしっかり獲得してくださいね。


サンプルダウンロード ⇒ コチラよりダウンロードできます。(別館)


■格言

アニメするには画像をまず準備する
Pictureプロパティをうまく活用して処理する


ビジュアル面がかなりグレードアップしましたね。






Chapter.39 [ 配列変数 ]

■変数のおさらい

今回は配列について解説します。

配列というのは、普段あまり聞きなれない言葉かもしれません。しかし、プログラミングにおいては非常に日常的で、当たり前のように使われている概念です。
配列を理解していると、コードが簡潔に、わかりやすく記述できるようになります。というかむしろ、配列を理解していないと、この先の講座を進めていくのが難しいです。それほどに、配列は重要なものなんですね。

今回は、この配列について詳しく解説します。できる限りわかりやすく解説するように心がけて書きますので、ゆっくりで構いませんから、しっかり覚えてくださいね。

まずは、配列を理解するうえで重要な、変数の考え方からおさらいしてみましょう。


変数は、データを入れておくための箱のようなものです。これは以前にも解説しましたね。そして、どのようなデータを入れておくための箱なのか、それを変数の型で表しました。

例えば『Integer型』というのは別名『整数型』と呼ばれている変数の型です。これは『-32768~32767』の範囲の整数を扱うことができる型でしたね。整数を扱う変数型ですから、小数点以下の数値を含んだデータや、文字列のようなデータを格納することはできません。

もし、文字列のようなデータを扱いたい場合には、『String型』の変数を使います。これは『文字列型』と呼ばれているデータ型でしたね。様々な文字のデータを扱うことができます。

このように、変数はその宣言された型の種類によって、格納できるデータの種類や大きさが異なります。そして、それを定義するために、プロシージャ内で『Dim』というキーワードを使って宣言するんでしたね。

例えば以下の例は、『Integer型』の変数、変数Intを宣言しています。

Dim Int As Integer

こうすることで、Intという変数が宣言され、コードの中で使うことができるようになります。先ほども書いたように、この変数はInteger型の変数なので、-32768~32767の範囲の整数を扱うことができます。
それ以上に大きい、あるいは小さい数値は扱うことができません。当然、文字なども扱うことができません。

どうですか? 上の説明を見ていて、スムーズに理解できましたか?
今回は配列についての解説を行いますが、まず、そのためには変数の仕組みを理解していることが前提になります。
もし、ここで少しでも不安が残る人は、以前の講座で詳しく解説していますので、まずはそちらをよくご覧になってください(Chapter6を参照)。
配列を理解するためには、変数についての最低限の知識が不可欠です。焦る気持ちを抑えて、基本に忠実に、ひとつひとつ習得してくださいね。


■本題、配列を紐解く

さて本題です。
配列というのは、一言で表すなら、『同じ型の変数の集まり』です。

これをスムーズに理解するために、こんな例え話を挙げてみます。

エクセルのワークシートに、100人分の顧客データが入力されているとする。
顧客のデータは、シートの列Aに入力されており、セルA1からセルA100まで、全て顧客の名前で埋まっている。

もし、この100人分の顧客データを、素直に変数に代入していくとすると、どんな風にコードを書けばいいのでしょうか。

1人目の顧客データを入れる変数を『Kyaku1』とすると、顧客の名前を入れる変数なのでString型で次のように宣言します。

Dim Kyaku1 As String

同様に、2人目からも順番に宣言していきます。

Dim Kyaku2 As String
Dim Kyaku3 As String
Dim Kyaku4 As String

    :
    :
    :

Dim Kyaku100 As String

…………気が遠くなりますね。
これはどう考えても変です。無理があります。

まだ100人ぐらいなら、気合でコピー貼り付けしていけばできるかもしれませんが、もし顧客データが10000人とかだったら、ハッキリ言ってやる気がおきません。変数の宣言を行うだけで、膨大なコードを書かなくてはならなくなってしまいます。

こんな時に活躍するのが『配列』です。
配列は、先ほども書いたように、『同じ型の変数の集まり』です。
今回の例で挙げたような、同様の変数型を使用するデータを扱う際には、配列を活用することで、簡潔でわかりやすい記述が可能です。

その使い方も至って簡単。
変数の宣言を行う際に、括弧を使って、要素の数を指定するだけです。
それが次のコード。

Dim Kyaku(99) As String

先ほどと同じことをしようとしただけですが、コードの記述はたった1行で済みます。これだけで、100人分の顧客名を格納できる配列変数が宣言できました。

通常の変数のときと同じように、『Dim 配列名(要素数) As 変数の型』というように記述します。
気をつけなくてはならないのは、要素数の指定の仕方です。

先ほど、要素数が100個ある配列を宣言しました。しかし、その要素数の部分は『99』となっていますね。これがよく間違えるところなので注意が必要です。
配列の要素は、基本的に『0』から始まります。ですから『Dim Kyaku(1) As String』と宣言していた場合には、『0と1』の2つの要素を持つ配列になります。先ほどの場合では、『99』という指定で配列を宣言していますので、その要素は『0から99』までの100個、ということになります。

もし1人目のデータを参照したい場合には『Kyaku(0)』です。
2人目なら『Kyaku(1)』ですね。

今回は100人分の顧客データを格納するための、要素数100の配列を宣言する方法を解説しました。
最も注意するべきところは、要素の最初の番号が0だというところです。
このことを踏まえて、コードに直してみましょう。

'セルA1からセルA100までが全て顧客名のデータで埋まっている。
'このデータを、繰り返し処理しながら、配列に格納する。

Sub Hairetu()
    Dim Kyaku(99) As String
    Dim L As Long
    For L = 0 To 99
        Kyaku(L) = Cells(L + 1, 1).Value
    Next
End Sub

ここで簡単にCellsについて解説しましょう。

Cellsは、ワークシート上のセルを表すことができます。括弧に続けて、行番号と列番号を指定することで、どのセルを参照するのか決められます。

Cells( 行番号 , 列番号 )』で、任意のセルを参照できるのですね。

今回の場合は、セルA1からセルA100までを順番に見ていくので、『Cells(1,1)からCells(100,1)』までを順番に処理すればいいのです。

先ほどのコードでは、For文を使って、100回の繰り返し処理を行っています。配列の要素は0から始まるので、繰り返し処理に『0から99まで』の100回という指定の仕方をしている点に注意しましょう。

Cellsでは、実際に存在するセルを参照するので、そのまま変数 L を使ってしまうとエラーが起きてしまいます。なぜなら、『Cells(0,1)』というセルは存在しないからですね。
そこで、先ほどの例ではセルの参照のときに、変数 L に1を足しているわけです。
あとは、セルの中に入力されている値を『セルのValueプロパティ』を使って各配列に格納していっているだけです。『セルのValueプロパティ』というのはセルに入力されている値そのものを表すので、これを配列に代入すればいいのですね。


■配列に関する補足

配列については理解できましたでしょうか。

同じデータ型の変数をたくさん用意しなくてはいけないようなケースでは、この配列が大いに活躍してくれます。
特に、先ほどの例で扱ったように、繰り返し処理と非常に相性がいい点が、配列の最大の利点です。ひとつひとつ地道に処理していくよりも、『繰り返し処理+配列』で、一気に処理してしまったほうが確実ですし簡単です。

基本的に、配列の要素が0から始まるという点がちょっとわかりにくいかもしれませんが、これは慣れの問題ですので、積極的に配列を活用しているうちに、自然と身についてくるでしょう。

実は、配列の最初の要素が0から始まるというこの仕組み、変更することもできます。
どうせなら1から始まってくれたほうがわかりやすいじゃ~ん、と、大抵誰もが始めは思います。実際私も最初のころ思いました。
そこで、その方法も簡単ですが紹介しておきます。

配列の要素を、0からではなく1から始めるようにするためには、モジュールの先頭部分で次のように記述します。

Option Base 1

はい、以上です。簡単ですね。

モジュールの先頭、一番上のところで、このように記述してある場合には、そのモジュールの中で宣言された全ての配列で、最初の要素の番号が1になります。
ですから、次のような場合にはどうなるかというと……

Option Base 1

Dim Kyaku(3) As String

これで要素を3つ持った配列Kyakuが宣言されます。
この配列は『Kyaku(1)Kyaku(2)Kyaku(3)』の3つの要素を持った配列です。Option Base 1を使ったことによって、配列の最初の要素が変化しているのがわかると思います。
もしOption Base 1を使っていないときに、同様の宣言の仕方をした場合には、『Kyaku(0)Kyaku(1)Kyaku(2)Kyaku(3)』という具合に、要素の数が4つになるわけです。配列の扱いに不慣れなうちは、このようにして直感的に使えるほうがいい場合もあるでしょう。

ちなみに、Option Baseを使って指定できるのは、0か1だけです。『Option Base 2』とか、『Option Base 3』とかは指定できません。

もし、プログラミングの都合上、0や1以外の要素から配列を定義したい場合には、『To』というキーワードを使って配列を宣言します。

例えば、5から10までの要素を持つ配列を宣言したいと考えたときは、次のように配列を宣言します。

Dim Kyaku(5 To 10) As String

Toというキーワードを活用すれば、任意の数値で要素数を指定した配列を宣言できます。正確には、配列の要素の『下限値と上限値』を指定できるわけですね。


■まとめ

配列についてまとめてみます。

配列とは同じデータ型の変数の集合体
配列の宣言には括弧を使う
括弧の中に要素数を指定する
通常は、配列の最初の要素は0から始まる
Option Baseを併用すると最初の要素を1にできる
任意の下限値と上限値を指定するときはToを併用する

配列の基本はこんなところです。基本とは言っても結構覚えることがたくさんありましたね。焦らず、しっかり覚えていってください。

配列は非常に奥が深く、ここで解説した以外にも、さらにたくさんの使い方や利用法があります。これらは、またいつか解説することになるでしょう。
とりあえず今回はここまでにしますが、今回の内容を理解してさえいれば、基本的な配列の概念については大丈夫です。さらに奥深い配列の世界ついてはまたいずれ。


■格言

同じデータ型をたくさん使用するなら配列を使用する
配列は繰り返し処理と相性がいい
配列の要素数には色々な指定方法がある


配列は変数と同じくらい重要な概念のひとつ。しっかり覚えてください。






Chapter.40 [ ゲームの初期化 ]

■ゲームの初期化

ユーザーフォームでゲームを作成する場合に限らず、ゲームの初期化というのはとても重要です。

例えば、以前、キャラクターをアニメーションさせて表示させる方法を解説しましたね。その方法とは、あらかじめ用意しておいたイメージコントロールの画像を、キャラクターとなるイメージコントロールに転送する、というものでした(Chapter38を参照)。

この方法を使う場合、アニメーションの原画となるイメージコントロールが必要です。そしてそれは、あらかじめユーザーフォーム内に配置しておく必要がありましたね。

以前の講座の場合、わかりやすくするために、プログラムの実行中にも原画となるイメージコントロールが表示されたままになっていました。しかし実際にゲームとして仕上げる段階になったら、この原画となるイメージコントロールは、画面に表示されないようにしておかないと不自然です。
ゲームをプレイする際に必要なのは、あくまでもキャラクター本体だけであり、原画となるイメージコントロールが表示されているのはなんだか変です。

原画となるイメージコントロールは、こういった理由からゲームの最中には表示されないようにしておく必要がありますね。ですから、ゲームの開始と同時に行う『初期化処理』というものを設け、そこで表示しないようにしてしまえばいいわけです。


このように、ゲームを作成する場合には、初期化処理が非常に重要です。
毎回必ず同じように動作させるためにも、ゲームの開始と同時に、必要な項目をキチンと設定するべきなのです。
今回はそんな観点から、ゲームの初期化について見ていくことにしましょう。


■ユーザーフォームの表示プロセス

今まで、何度もユーザーフォームを扱っていますが、ユーザーフォームの起動に関する細かいプロセスについては解説してきませんでした。
この理由としては、とりあえず知らなくてもなんとかなる、というのが第一です。あまりプログラミングに親しくないときに、いきなりフォームの起動プロセスがなんたらかんたら……と言われても、なかなか理解できないのではないかと、私自身が考えたからでした。

しかし、ゲームの初期化を考える場合、特に、ユーザーフォームを使ったゲームの初期化を考える場合には、これを解説しなくてはなりません。

それほど難しい問題ではないので、ここまで順に進めてきてくれた方なら、特に問題なく理解できるはずです。焦らずゆっくり、今回の内容を進めてみてください。

ユーザーフォームが表示されてから、閉じられるまでの一連の処理は、おおよそ次のような感じで処理されています。

メモリ上にユーザーフォームがロードされる
    ▼
ユーザーフォームの表示前処理
ここでInitializeイベントが発生する
    ▼
ユーザーフォームが表示されアクティブになる
ここでActivateイベントが発生する
    ▼
何らかの方法でユーザーフォームが閉じられようとすると……
    ▼
ユーザーフォームが閉じる前の処理
ここでQueryCloseイベントが発生する
    ▼
ユーザーフォームが非表示になる
    ▼
ユーザーフォームの各種インスタンスがメモリから開放される
    ▼
ユーザーフォームのTerminateイベントが発生する

かなりわかりにくいことになっていますね。

ユーザーフォームはまず、メモリ上にロードされます。これから先、プログラム上で様々な処理を行うときには、このメモリを参照しながら処理が進みます。
メモリ上にフォームがロードされると、次にInitializeというイベントが発生します。そしてそのあと問題がなければユーザーフォームが実際に画面上に表示されます。表示された時点で、Activateイベントというイベントが発生します。

これで表示処理が完了。結構長いですね。

そして、フォームが閉じられようとすると、今度は閉じるときの処理が始まります。

まずは、閉じられようとした瞬間に、QueryCloseイベントが発生します。以前の講座で、このイベントに、全ての処理が止まるようにプログラミングをしましたよね。
これが終わると、フォームが非表示になり、同時にメモリ上にあったフォームのデータに関するインスタンスが開放されます。ただし、この時点ではまだ完全に処理が終了したわけではありません。最後にTerminateというイベントが発生します。このイベントが終わった段階で、ユーザーフォームを閉じる処理が完了します。う~ん、わけわかりません。
私自身がよくわかっていない部分もあるので、ちょっと言い回しや考えがおかしいところがあるかもしれません。気がついた人、教えてください。


■発生するイベントに注目する

なんだか先ほどのプロセスを見ると、ちょっとビビッてしまいますが、焦ってはいけません。大事なことはそれほど多くありませんので、ゆっくり見ていきましょう。

ここで大切なのはメモリ云々の話ではなく、発生するイベントのタイミングです。
ユーザーフォームが表示されて、閉じられるまでの間に、イベントが4つ発生していますね。これが今回の肝です。

イベントだけ抜き出して並べると、次のようになります。

①UserForm1.Initialize
    ▼
②UserForm1.Activate
    ▼
③UserForm1.QueryClose
    ▼
④UserForm1.Terminate

①から順番に見てみましょう。

Initializeイベントは、ユーザーフォームが表示される直前に発生するイベントです。
実は今回のテーマである『初期化』に大きく関わるイベントです。ユーザーフォームが表示される直前に発生するので、表示する前に何かやっておきたい場合には、このイベントを利用して処理を行います。

②ユーザーフォームがアクティブになったときに発生します。まぁイメージとしては表示された瞬間ですね。

③は、以前にも登場していますね。ユーザーフォームが閉じられる直前に発生するイベントです。もしも、閉じてしまう前に何かやっておきたいときなどは、このイベントを利用します。
以前、このイベントを使ってコードが全て止まるようにプログラミングしましたね。

④、Terminateはちょっと特殊です。ユーザーフォームを閉じる処理が一通り終わってから発生します。
ですからこのイベントが発生した時点では、すでにフォームは表示されていません。


今回のテーマ初期化処理は、先ほども書いたようにInitializeイベントに記述します。ゲーム画面となるユーザーフォームが表示される前に、初期化するべきところを全て処理してしまうように、コードを書きます。

それでは、具体的に初期化処理とはどのようなことを指すのでしょうか。


■ゲームの初期化するべき項目

ユーザーフォームでゲームを作成する場合には、見せたくないものを隠すのに、よくInitializeイベントを使います。

例えば以前アニメーション処理の解説をしたとき、このようなデザインのユーザーフォームを使いました。画像は以前の解説に使ったものをそのまま引用しています。

191.gif

しかし、このままでは、原画として使うイメージコントロール(Image1~3)も表示されたままになっています。ですからInitializeイベントが発生したときに、隠してしまうようにしてみましょう。

今回はアニメーションのときに解説したコードをそのまま流用します。もし、細かいところがわからないよ、という人は、以前の講座を先にご覧ください。そちらで詳しく解説しています(Chapter38を参照)。

前々回のコードでは、フォームモジュールに『コマンドボタンのClickイベント』と、『ユーザーフォームのQueryCloseイベント』の2つが記述されているはずです。
ここに、もうひとつのイベントプロシージャを追加します。それが『フォームのInitializeイベント』です。

'コマンドボタンのClickイベント
Private Sub CommandButton1_Click()
    Call Anim_Test
End Sub

'ユーザーフォームのQueryCloseイベント
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    End
End Sub

'ユーザーフォームのInitializeイベント
Private Sub UserForm_Initialize()
    CommandButton1.Left = Ima_Chara.Left '①
    UserForm1.Width = 150 '②
End Sub

上の2つのイベントプロシージャは前々回のときと全く同じです。一切、変更はしていません。
今回大事なのは、最後に追加された『ユーザーフォームのInitializeイベント』です。これが、初期化処理を行っている部分ですね。

具体的には何をしているかというと、『コマンドボタンの位置の変更』と、『ユーザーフォームの横幅の変更』です。
まず①の部分で、コマンドボタンの表示位置を変更しています。これは画像で表すとわかりやすいのではないでしょうか。

200.gif



201.gif

コマンドボタンの横位置(つまりLeftプロパティ)に、Ima_Charaというイメージコントロールの横位置を設定しています。つまり、この2つのコントロールの左端の部分が全く同じ横位置になったわけです。

次に②の部分で、ユーザーフォーム自体の横幅を変更して、少し狭くしています。
こうすることで、結果として、原画となる3つのイメージコントロールが見えなくなるという寸法です。

202.gif

あくまでも、ユーザーフォームのInitializeイベントでこの処理を行っていますので、もともとのレイアウトを変更する必要はありません。
このInitializeイベントを追加した状態で、ユーザーフォームを表示してみてください。キチンと表示される前に処理が行われ、コマンドボタンの位置変更と、ユーザーフォームの横幅変更が行われているはずです。

203.gif



■その他の初期化処理

先ほどのようにInitializeイベントをうまく活用すると、フォームのレイアウトをゲームの開始と同時に初期化することができます。

しかし、初期化しておいたほうがいい項目は、ユーザーフォームのレイアウトだけではありません。

例えば、Publicを使って宣言した変数の初期化などがそうです。
Publicで宣言した変数は、そのスコープがアプリケーション全体に及びます。これは言い換えると、Excel自体が終了するまでは、自動的に初期化されることはない、ということでもあるのです。

ですからPublic宣言された変数は、いったんコードの実行が止まってしまっても、コード内で設定したデータの内容が残ったままになっています。
もし、もう一度コードが実行されたときに、以前のデータが残っていてしまったら、これによって不具合が発生しないとも限りません。ですからこのようなケースでは、必ず最初に変数の状態も初期状態に戻すようにしておいたほうがいいでしょう。
つまり、Publicで宣言した変数など、以前のデータが残っている可能性があるものに関しては、Initializeイベントを使って、本来の初期値に戻すようにしておけばいいわけです。
ゲームの途中経過を、ワークシート上のセルに出力して残している処理などでも、最初にその範囲をクリアするようにしておけば、ゲーム起動後に不具合が出ることはないでしょう。

自分がどのような設計をしているのかをよく把握して、初期化するべきところは、フラットな元の状態に戻しておく癖をつけましょうね。


■格言

初期化にはInitializeイベントを活用する
Public宣言の変数なども最初に初期化しておく


繰り返し起動しても、不具合の出ないプログラムを設計しましょう。






メールフォーム

影斬に物申すという方はこちら

名前 :
メール:
件名 :
本文 :

可能な限り要望には応えますが、必ず返信や回答ができることを、保障するものではありません。
ご了承ください。

Chapters

コンテンツ一覧


■Chapter 一覧■
    全てのChapterの一覧です。
    直接アクセスしたい方はこちらをご利用下さい。

    Chapter.1 [ 知っておくべき心得 ]
    Chapter.2 [ Excelってなんだろう ]
    Chapter.3 [ Excelの基本画面 ]
    Chapter.4 [ VBAとは? ]
    Chapter.5 [ モジュールについて ]
    Chapter.6 [ 変数 ]
    Chapter.7 [ 変数の型と宣言 ]
    Chapter.8 [ プロシージャとスコープ ]
    Chapter.9 [ ゲームつくる様々な手法 ]
    Chapter.10 [ ユーザーフォーム ]
    Chapter.11 [ プロパティウィンドウ ]
    Chapter.12 [ 乱数 ]
    Chapter.13 [ 条件分岐 ]
    Chapter.14 [ ゲーム画面のデザイン ]
    Chapter.15 [ コード記述の基本作法 ]
    Chapter.16 [ じゃんけんゲーム:1 名前をつける ]
    Chapter.17 [ じゃんけんゲーム:2 フォームの起動 ]
    Chapter.18 [ じゃんけんゲーム:3 乱数の種 ]
    Chapter.19 [ じゃんけんゲーム:4 イベント ]
    Chapter.20 [ じゃんけんゲーム:5 引数 ]
    Chapter.21 [ じゃんけんゲーム:6 役判定 ]
    Chapter.22 [ じゃんけんゲーム:7 予測と制限 ]
    Chapter.23 [ Withステートメント ]
    Chapter.24 [ 画像を表示させる ]
    Chapter.25 [ 画像表示の発展形 ]
    Chapter.26 [ 繰り返し処理 For文 ]
    Chapter.27 [ 繰り返し処理 Do~Loop文 ]
    Chapter.28 [ Exitステートメント ]
    Chapter.29 [ フォーム上の位置情報 ]
    Chapter.30 [ API基礎知識 ]
    Chapter.31 [ API補足知識 ]
    Chapter.32 [ メインループを考える ]
    Chapter.33 [ 同期処理の概念 ]
    Chapter.34 [ 移動処理その1:画面設定と考え方 ]
    Chapter.35 [ 移動処理その2:DoEvents ]
    Chapter.36 [ 移動処理その3:キー入力判定API ]
    Chapter.37 [ 条件分岐のさらなる探求 Select Case ]
    Chapter.38 [ アニメーション ]
    Chapter.39 [ 配列変数 ]
    Chapter.40 [ ゲームの初期化 ]
    Chapter.41 [ シューティングゲーム1:ゲーム設計 ]
    Chapter.42 [ シューティングゲーム2:メインプロセス ]
    Chapter.43 [ シューティングゲーム3:構造体 ]
    Chapter.44 [ シューティングゲーム4:定数 ]
    Chapter.45 [ シューティングゲーム5:プレイヤーキャラクター ]
    Chapter.46 [ シューティングゲーム6:ショットを撃つ① ]
    Chapter.47 [ シューティングゲーム7:ショットを撃つ② ]
    Chapter.48 [ シューティングゲーム8:Mod演算子の活用 ]
    Chapter.49 [ シューティングゲーム9:敵キャラクター登場 ]
    Chapter.50 [ シューティングゲーム10:衝突判定 ]
    Chapter.51 [ シューティングゲーム11:衝突の実体 ]
    Chapter.52 [ シューティングゲーム12:敵の攻撃 ]
    Chapter.53 [ シューティングゲーム13:爆発エフェクト ]
    Chapter.54 [ シューティングゲーム14:残機数表示① ]
    Chapter.55 [ シューティングゲーム15:残機数表示② ]
    Chapter.56 [ シューティングゲーム16:スコアの表示 ]
    Chapter.57 [ シューティングゲーム17:タイトル画面 ]
    Chapter.58 [ シューティングゲーム18:ボスキャラクター ]
    Chapter.59 [ シューティングゲーム19:最後の仕上げへ ]
    Chapter.60 [ シューティングゲーム20:いよいよ完成STG ]
    Chapter.61 [ カードゲームで使えるめくり効果 ]
    Chapter.62 [ ラジアンと角度 ]
    Chapter.63 [ ラジアンの活用:円運動 ]
    Chapter.64 [ ラジアンの活用:任意の角度へ移動する ]
    Chapter.65 [ APIによるサウンド再生:基礎 ]
    Chapter.66 [ APIによるサウンド再生:MIDIと多重再生 ]
    Chapter.67 [ APIによるサウンド再生:MCIコマンドとループ再生 ]
    Chapter.68 [ Function プロシージャ ]
    Chapter.69 [ 値渡しと参照渡し ]
    Chapter.70 [ デバッグ1:イミディエイトウィンドウ ]
    Chapter.71 [ デバッグ2:ローカルウィンドウ ]
    Chapter.72 [ デバッグ3:コード実行の中断 ]
    Chapter.73 [ オブジェクトってなんだ ]
    Chapter.74 [ プロパティ・メソッド・イベント ]
    Chapter.75 [ オブジェクト変数 ]
    Chapter.76 [ オブジェクトとコレクション ]
    Chapter.77 [ 特殊な繰り返し:For Each ]
    Chapter.78 [ エラー処理 ]
    Chapter.79 [ On Error と GoTo文 ]
    Chapter.80 [ Resumeステートメント ]
    Chapter.81 [ バイトとビット ]
    Chapter.82 [ ウィンドウメッセージとイベント ]
    Chapter.83 [ 文字列の基礎 ]
    Chapter.84 [ 文字列操作① ]
    Chapter.85 [ 文字列操作② ]
    Chapter.86 [ タイピングゲーム1:仕様を決める ]
    Chapter.87 [ タイピングゲーム2:キー入力検知 ]
    Chapter.88 [ タイピングゲーム3:文字列照合 ]
    Chapter.89 [ タイピングゲーム4:判定関数 ]
    Chapter.90 [ タイピングゲーム5:ゲーム画面設計 ]
    Chapter.91 [ タイピングゲーム6:問題文のソート ]
    Chapter.92 [ タイピングゲーム7:動的配列 ]
    Chapter.93 [ タイピングゲーム8:キーダウンイベント ]
    Chapter.94 [ タイピングゲーム9:正打数の表示 ]
    Chapter.95 [ タイピングゲーム10:タイムの表示 ]
    Chapter.96 [ クリックゲーム1:イベントの種類 ]
    Chapter.97 [ クリックゲーム2:画面設計 ]
    Chapter.98 [ クリックゲーム3:クリック座標検知 ]
    Chapter.99 [ クリックゲーム4:キャラクター準備 ]
    Chapter.100 [ クリックゲーム5:キャラクターの配置 ]
    Chapter.101 [ クリックゲーム6:キャラクター移動とNot演算子 ]
    Chapter.102 [ クリックゲーム7:クリックのヒット判定 ]
    Chapter.103 [ クリックゲーム8:ヒットマークエフェクト ]
    Chapter.104 [ クリックゲーム9:サウンド処理の実装 ]
    Chapter.105 [ クリックゲーム10:マウスカーソルの変更 ]
    Chapter.106 [ ブロック崩しゲーム1:仕様と概要を決める ]
    Chapter.107 [ ブロック崩しゲーム2:基本概念の確認 ]
    Chapter.108 [ ブロック崩しゲーム3:ベクトルとは ]
    Chapter.109 [ ブロック崩しゲーム4:変数や定数の宣言 ]
    Chapter.110 [ ブロック崩しゲーム5:初期化処理の実装 ]
    Chapter.111 [ ブロック崩しゲーム6:ブロックの配置 ]
    Chapter.112 [ ブロック崩しゲーム7:根幹処理とバーの処理 ]
    Chapter.113 [ ブロック崩しゲーム8:線分と線分の交差を判定 ]
    Chapter.114 [ ブロック崩しゲーム9:線分同士の交点 ]
    Chapter.115 [ ブロック崩しゲーム10:ボールの処理 ]
    Chapter.116 [ ブロック崩しゲーム11:最終調整して完成へ ]
    Chapter.117 [ テキストファイル操作基礎 ]
    Chapter.118 [ テキストファイル操作:読み込み編 ]
    Chapter.119 [ テキストファイル操作:CSV読み込み編 ]
    Chapter.120 [ テキストファイル操作:様々な読込編 ]
    Chapter.121 [ テキストファイル操作:バイナリ編 ]
    Chapter.122 [ テキストファイル操作:暗号化編 ]
    Chapter.123 [ テキストファイル操作:復号化編 ]
    Chapter.124 [ クラスモジュールとは ]
    Chapter.125 [ クラスモジュール:メソッド編 ]
    Chapter.126 [ クラスモジュール:プロパティ編 ]
    Chapter.127 [ クラスモジュール:イベント拡張編 ]
    Chapter.128 [ クラスモジュール:イベント自作編 ]
    Chapter.129 [ APIによる描画処理1:ハンドル ]
    Chapter.130 [ APIによる描画処理2:デバイスコンテキスト ]
    Chapter.131 [ APIによる描画処理3:ペン オブジェクト ]
    Chapter.132 [ APIによる描画処理4:ブラシ オブジェクト ]
    Chapter.133 [ APIによる描画処理5:図形描画準備編 ]
    Chapter.134 [ APIによる描画処理6:図形描画実践編 ]
    Chapter.135 [ APIによる描画処理7:画像描画の仕組み編 ]
    Chapter.136 [ APIによる描画処理8:ビットブロック転送編 ]
    Chapter.137 [ APIによる描画処理9:ラスタオペレーション ]
    Chapter.138 [ APIによる描画処理10:マスク描画 概念編 ]
    Chapter.139 [ APIによる描画処理11:マスク描画 実践編 ]


    コードやVBAに関する質問などはサポート掲示板(別館)までお気軽にどうぞ。




fc2 seotool Excel VBA ゲーム プログラミング 講座

Counter

twitter


Shadow BBS - 影掲示板

VBA 関連書籍



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。