見出し画像

Excel(VBA)で簡単なゲームを作る(プログラムコード解説編)

こんにちは「つけらっとゲームス」プログラム担当のとちです。
今回は「Excel(VBA)で簡単なゲームを作る」で作ったゲームのプログラムを解説したいと思います。

続きの記事になりますので前回をご覧になっていない方はコチラからどうぞ

Unityなどのゲームエンジンを使うのは難しそう、もうちょっと馴染みのあるソフトを使ってゲーム作れないかなーっていう方に向けての記事です。
プログラム全体も前述の記事にありますので、そちらを参照ください。

今回の記事ではプロシージャに切り分けて解説していきます。

なお、この記事のExcelについてですが、
普段使っているPCに入っていたMicrosoft office Home and Business 2016で作ったモノなので皆さんのお手元のExcelと微妙に異なるかもです。
ご了承ください。



冒頭部分の解説

まずはオレンジの四角で囲まれたところから説明しますね。

最初の1行目にある「Option Explicit」ですが、
これは「Microsoft Learn」の該当ページから引用すると…

Dim、Private、Public、ReDim、または Static ステートメントを使用して、すべての変数を明示的に宣言する必要があります。 宣言されていない変数名を使用しようとすると、コンパイル時にエラーが発生します。
(中略)
※注意
Option Explicit を使用すると、既存の変数の名前を入力する場合の誤入力や、コード内で変数のスコープが明確でない場合の混乱を避けることができます。

Microsoft Learn - Visual Basic for Applications [Option Explicit ステートメント]

プログラムの中で使用する変数は自分で名前を決めることができますが、テキトーに作っちゃうと混乱を招く恐れがあります。それを防いでくれる「おまじない」くらいに考えておきましょう。

続いて、この部分…

Dim dOBJ1(5) As Object                  '[変数定義]サイコロ1の出目オブジェクト配列
Dim dOBJ2(5) As Object                  '[変数定義]サイコロ2の出目オブジェクト配列

これはサイコロのイメージオブジェクトを変数配列として定義しています。
「Dim」は変数の宣言を意味し「dOBJ1」「dOBJ2」は変数の名前です。

ひとつのサイコロにつき、6パターンの出目があります。1個目からではなく、0個目から5個目まで入るので(5)という表記になっています。

とはいえ、ここは変数配列の長さ(配列長)を意味する数字なので、実は(6)と入れても、6個目の場所が空いているだけで(念のため動かしてみましたが)問題なく動きます。

言語によっては6パターン使用するので、配列長が6であると定義する必要があります。今回の記事(VBA)では0から5まで使用するという定義です。

「As Object」は、この変数配列がオブジェクト型だと示しています。
サイコロの出目画像を持っている「イメージオブジェクト」を変数配列で持っています。


Private Sub UserForm_Initialize()

次は下のスクショのオレンジ四角の中です。

緑色の文字はコメントです。プログラムコードの中にありますが動作はしません。自分のメモや修正箇所などを残して置くときに使用します。

コメントは残しておくと良いです。
その行で何をしているのかもコメントとして残しているので、それを踏まえてお付き合いください。

ここではコメントのとおり「UserForm1」を初期化しています。

プロシージャとイベント

次の部分「Private Sub UserForm_Initialize()」ですが…

プログラムコードはプロシージャにまとめられています。
とりあえず「Sub」とか「Fanction」をプロシージャだと思ってください。
「Private Sub」は同モジュール内の中からは呼び出せるプロシージャです。
「Public Sub」になると他モジュールからも呼び出せます。

何故「Private」「Public」のように機能が分かれているのか今は深く考えない方がいいです。プログラム上での事故を防ぐ工夫のひとつ…くらいに考えて置けばいいでしょう。

「UserForm_Initialize()」の部分は
「UserForm」はゲーム画面のフォームになっています。
「Initialize()」は初期化時(初期化イベントの発生時)に動きます。

ちなみに「Private Sub UserForm_Click()」だと?
「UserForm」がクリックされた時…
つまり、クリックイベントの発生時ということですね!

Randomize

これで乱数シードを初期化します。
システムタイマーから返された値をシード値としているそうです。
現時点では、あんまり深く考えずに「乱数を使う時は書いておく」くらいで覚えておけば良いでしょう。

Set dOBJ1(0) = dImage11

ここから「Set」から始まるコードが並んでいます。
これは冒頭で定義した変数配列にイメージオブジェクトを代入している部分です。変数配列は(0)から始まるので、出目1を(0)に入れて、その後は順番に出目6まで格納しているわけです。

サイコロ2つ使用していますので、Set文は12個あるんですね。

fDiceFormat

これは「Function」である「DiceFormat」を呼び出しています。
内容はコメントに書いてあるとおり、サイコロ画像を全て非表示にします。

dOBJ1(0).Visible = True

これと、これの次の行の「dOBJ2(0).Visible = True」は、変数配列の先頭の表示を許可するという意味です。
英単語で「visible」を日本語に訳すると「見える」です。

プロパティ欄にも「Visible」を設定する欄がありますね。
ここを「True」にすると見えて、「False」にすると非表示になるんです。

それぞれの変数配列の先頭には「出目1」のイメージオブジェクトが入っています。つまり画面の初期化時にサイコロ2つは「1」が表示されるという指示をしているんですね。

End Sub

これが出てくると、このプロシージャは終わりだよーって意味です。


Private Sub BeforeSeven_Click()

UserForm初期化時の処理が終わった後に書いてあるのがコマンドボタンそれぞれをクリックしたときの処理です。

「Private Sub BeforeSeven_Click()」は「6以下のボタン」をクリック!
「Private Sub JustSeven_Click()」は「7のボタン」をクリック!
「Private Sub AfterSeven_Click()」は「8以上のボタン」をクリック!

というプロシージャが並んでいます。
それぞれの内容をよく見ると似ていますね。

fAnswer (x)

それぞれのコマンドボタンをクリックしたときには、
Function fAnswer(BtnSw As Integer)プロシージャを呼び出しています。
上のスクショではオレンジ枠の下のプロシージャがそれに当たります。

fAnswerの後の(6)は、6以下のボタンを押したことを意味しています。
(7)は、7のボタンをクリックしたという意味ですし、(8)は8以上のボタンを押したことを意味しています。

このカッコの中の数字を引数といいます。
呼出先のプロシージャで使用するフラグのようなものだと考えてください。


Function fAnswer(BtnSw As Integer)

次のプロシージャを見ましょう!
ここでは「ゲーム結果を画面反映する」処理を行います。

「BtnSw As Integer」の部分は「fAnswer」を呼び出した際の引数が入る変数となっています。
「fAnswer(6)」の場合は、変数「BtnSW」に「6」が入ることになります。

(a)の部分

スクリーンショットのオレンジ枠の中で言えば(a)の部分の説明です。
ここから3つの変数定義が続きます。
「Dim wDice As Integer」サイコロ2個の出目合計値を入れる変数です。
「Dim wSW As Integer」ゲーム結果を入れる変数です。
「Dim wTxt As String」ゲーム結果のテキストを入れる変数です。

「Integer」は整数の変数型です。
-32,768 ~ 32,767までの整数に使用できます。

「String」は文字列の変数型です。
文章を入れるときに使用するとよいでしょう。

(b)の部分

引き続き、スクショのオレンジ枠の中で言えば(b)の部分の説明です。
ここは処理の前に、変数に対して準備をしている部分です。

「wDice = fDiceRoll()」
これはFunction fDiceRoll()プロシージャを呼び出しています。変数「wDice」に代入される「戻り値」はサイコロ2個の出目を合計したものです。

「wSW = 1」
先ほど定義したゲーム結果を入れる変数です。
0が入っていれば「あたり」です。1が入っていれば「はずれ」です。
この時点で「はずれ」に設定しています。

「wText = "残念でした…"」
先ほど定義したゲーム結果のテキストですが、「はずれ」の時に表示するメッセージを入れておきます。

「NowCoin.Caption = NowCoin.Caption - 1」
NowCoinはゲーム画面でコイン数を表示しているラベルオブジェクトです。
(b)の部分は、これまでも「はずれ」た場合の準備をしています。
ですので、コインの数も「はずれ」た場合とし、コインを1枚減らします。

(c)の部分

引き続き(c)の部分を説明していきます。

「If BtnSw = 6 And wDice <= 6 Then wSW = 0」
Ifは条件分岐です。引数であるBtnSwが6であり、且つ(And)、wDice(サイコロ2個の出目合計)が6以下である場合、wSW(ゲーム結果)を0「あたり」としています。

「If BtnSw = 7 And wDice = 7 Then wSW = 0」
「If BtnSw = 8 And wDice >= 8 Then wSW = 0」
同じように引数であるBtnSWの数値と、wDice(サイコロ2個の出目合計)が条件通りであれば、wSW(ゲーム結果)を0「あたり」としています。

(d)の部分

「If wSW = 0 Then」
ここでもIf文があります。
これまでの処理でwSW(ゲーム結果)が0「あたり」ならば…という意味です。
(c)のIf文とは異なり、この後で処理が2つ(複数)あります。
こういった場合は「End If」まで、当該条件ならば…という意味になります。

「wTxt = "おめでとう!"」
予測が当たった場合のメッセージです。

「NowCoin.Caption = NowCoin.Caption + 2」
NowCoinはゲーム画面でコイン数を表示しているラベルオブジェクトです。
(b)の部分で、はずれた場合の処理としてコインを1枚減らしていますので、当たった場合は2枚増やしています。結果的に当たった場合は+1枚になりますよね!

(e)の部分

「wTxt = "出目は「" & wDice &  "」" + wTxt」
ここまでの処理でwTxtには「おめでとう!」か「残念でした…」が入っています。wDiceはサイコロ2個の出目合計です。
つまり、出目と結果をくっつけてゲーム結果テキストを編集してます。

(f)の部分

ここで2つの「If~End If」文があります。

前の方のIf文では「NowCoin.Caption」(ゲーム画面のコイン数を表示しているラベルオブジェクトのCaption)の中身が0枚以下の場合の処理を書いています。

後の方のIf文では「NowCoin.Caption」が10枚以上の場合の処理です。

If文の中身は「MsgBox "Game Over"」または「MsgBox "Game Clear"」と「End」になっていますね。

「MsgBox」を使うと画面にメッセージボックスを表示します。その後に続く"Game Over"または"Game Clear"をメッセージボックスとして表示する処理です!

「End」
クリアまたはゲームオーバーなのでゲームを終了させています。


Function fDiceRoll()

かなり長くなってきましたが、もう少し頑張りましょう!
次のプロシージャです。ここではサイコロを2つ振った時の出目合計を返答する処理をしています。

(a)の部分

「Dim dOut1 As Integer」1個目のサイコロの出目を格納する変数です。
「Dim dOut2 As Integer」2個目のサイコロの出目を格納する変数です。
ここでは変数を定義しているだけですね。

(b)の部分

「fDiceFormat」
以前も出てきましたね。
サイコロ画像を全て非表示にするプロシージャを呼び出しています。

「dOut1 = fRandom(6, 1)」
「dOut2 = fRandom(6, 1)」
Function fRandom(dMax As Integer, dMin As Integer)を呼び出しています。
これは乱数を用いてサイコロを表現しているプロシージャです。
引数として最大値6から最小値1を引き渡しています。
dOut1 または dOut2 に対して戻り値を代入します。

(c)の部分

「dOBJ1(dOut1 - 1).Visible = True」
「dOBJ2(dOut2 - 1).Visible = True」
戻り値である dOut1 及び  dOut2 から1つ減算したモノを添え字としサイコロのイメージオブジェクトのVisibleをTrue(つまり、表示可)にしています。

事前に「fDiceFormat」で全て非表示にしているので出目を画面表示する処理をしています。

「fDiceRoll = dOut1 + dOut2」
サイコロ1と2の出目を足し算して、fDiceRollに代入しています。
これを戻り値として呼び出し元に返答しています。

「Beep」
これがあると音がなります。
「うるさい!」と思ったら、この行だけ消すと静かになりますよ。


Function fDiceFormat()

残るプロシージャは、あと2つ…もう少しだけ説明が続きます!

「Dim i As Integer」変数を定義しているだけですね。
ループ処理の添え字(index)として使用します。

「For i = 0 To 5」ループ文です。
「For」から3行下の「Next」までを繰り返します。
繰り返す条件が「i = 0 To 5」です。i という変数が0から5までの間、処理を繰り返します。

「dOBJ1(i).Visible = False」
「dOBJ2(i).Visible = False」
ループと添え字(index)を利用し、6面全部のサイコロ画像に対してVisibleをFalse(つまり非表示)にしています。


Function fRandom(dMax As Integer, dMin As Integer)

最後のプロシージャです。
引数としてdMax(最大値)とdMin(最小値)を呼び出し元から引き継いでいます。

「fRandom = Int(dMax * Rnd + dMin)」
得た乱数をfRandomに入れています。これで戻り値として使用できます。

Intのカッコに数字を入れると、カッコ内の整数の値を返答します。

Rndは、0以上、且つ1未満の値を返します。
なのでサイコロを表現するにはどうしたらよいでしょう?

この例では、Rnd 関数を使用して、1 から 6 までのランダムな整数を生成します。

Dim MyValue As Integer
MyValue = Int((6 * Rnd) + 1) ' Generate random value between 1 and 6.

Microsoft Learn - Visual Basic for Applications [Rnd 関数]

…ですね!


改造してみる?

プログラムの全体像をみたら、それほど複雑じゃないのがわかりますね。
では、少しだけ改造してみましょう。

このゲームではサイコロ2個の出目合計が
「6以下」「7」「8以上」と3つの選択肢になっています。
予想が正解すればコインが1枚増えますが、この3つ、同じ割合で発生するのかというと実は偏りがあるんですね。

「6以下」が出る確率は約42%
「8以上」が出る確率は約42%
「7」が出る確率は約17%

なので「7」に賭けると分が悪いのです。

そこで「7」を予測した場合に「あたり」だった場合、コインを2枚増やすように改造してみましょう。

やり方は色々あります!

しかし、いきなり言われても難しいですよね?
では、やり方のひとつを以下に貼り付けます!

ここだけ(赤枠を追加する)でOKです!
何をやっているのかというと…

この部分をプログラムが通るのは予測が当たっている場合だけです。
予測が当たっていて、BtnSwが「7」の場合は出目が7ですよね?

その場合はコインを更に+1個しているだけです。
思ったより簡単ですよね!

改造する方法がわかったら、あとは自分好みに変更していきましょう。
これで何かを作るのが楽しいことだと感じて貰えたら嬉しいです。

それでは、また別の記事でお会いできるのを楽しみにしております!


関連記事について

「ゲームを作ってみたいけど、どうしたら作れるの?」にお答えする記事は他にもあります。ご興味がございましたら下記の関連記事もご覧ください。

(↓)アナログなゲームを作ってみてもいいんじゃない?っていう記事

(↓)ゲームブックを作ったので試遊だけでもいかが?っていう記事

(↓)クォリティはともかくカードゲームも作ってみたよって記事


この記事が参加している募集

#ゲームの作り方

877件

この記事が気に入ったらサポートをしてみませんか?