その昔 倉庫番 というゲームがありました。
Excelで完全に再現することは不可能ですが、できるだけ再現してみたいと思います。
ゲームプログラミングを勉強したい方には、あまり参考にならないと思います。
しかし、VBAの学習に多少寄与できるよう無駄なことをしながら挑戦してみます。
途中で挫折するかもしれませんので、ご承知ください。
シート設定と画像ファイルの用意
前回記事の続きです。
今回は使用するシートはひとつのみです。
ゲームに近づけるため画像ファイルを使用しています。
画像サイズは縦30px、横30pxで、テンプレートは壁が1つ、荷物用に目的地に置く前のものと目的地に置いたあとのものの2つ、目的地が1つ必要です。
作業者用の画像は4方向向けに4つ用意しましたが、1つだけでも可能です。
また、私は同じ画像を回転したものを4つ用意しているだけなので、Rotationプロパティを使うのもひとつです。
画像(シェイプ)はセルの中でなくセルの上に用意しています。
それぞれのシェイプ名は前回記事をご覧ください。
account-it-dentist.hatenablog.com
なお、画像を使用せずにセルの色や文字(セルの値)で表現するほうがVBAらしく、作成も容易になります。
シートは正方形の方眼紙にします。
画像ファイルを使用する場合は、そのサイズに合わせて列の幅=3.13、行の高さ=22.5に設定します。
説明やステップ数を、壁で囲まれた領域の右側に用意していますが、2面・3面と作る予定なら上側や左側に用意したほうがいいでしょう。
壁で囲まれた領域を「マップ」と呼ぶことにしますが、マップの大きさは一定ではなく、マップに依存しない場所が好ましいです。
「ステップ数」とは作業者が移動した回数で、操作しても壁などに阻まれた場合はカウントしません。
時間を計測しないので、このステップ数を少なくクリアすると優秀ということです。
マップの設計図とコード
「マップ」を壁で囲まれた領域といいましたが、正確には長方形領域です。
ゲーム開始時をシェイプ名で表現するとこのようになります。
ゲームのやり直し時もこの状態でスタートします。
この情報をプログラム内で持たせようと思います。
ここからは標準モジュールのコード紹介です。
マップ領域範囲およびステップ数セルの行番号および列番号を定数で定義しています。
'定数定義 Const C_MIN_ROW As Integer = 2 'マップ左上(行) Const C_MIN_COL As Integer = 2 'マップ左上(列) Const C_MAX_ROW As Integer = 9 'マップ右下(行) Const C_MAX_COL As Integer = 10 'マップ右下(列) Const C_CNT_STEP_ROW As Integer = 2 'ステップ数セル(行) Const C_CNT_STEP_COL As Integer = 15 'ステップ数セル(列)
マップ領域範囲、ステップ数セルともに範囲名(名前)を使うほうが一般的だと思います。
例えばマップ領域を「MAP01」と名付けたとすると、行番号および列番号は
- Range("MAP01").Row '左上の行番号
- Range("MAP01").Column '左上の列番号
- Range("MAP01").Rows(.Rows.Count).Row '右下の行番号
- Range("MAP01").Columns(.Columns.Count).Column '右下の列番号
で取得できます。
"Range" はオブジェクト変数に格納できますので、今後触れていきます。
範囲で有名なプロパティには CurrentRegion や UsedRange があります。
また選択範囲を変えたいときには Resize を使います。
今回は触れる機会がないと思いますので、勉強中でまだ使ったことがない方は調べてみてください。
構造体
この情報を構造体配列に格納していきたいと思います。
構造体をユーザー定義型と呼ぶ人もいます。
わざわざ構造体に格納なんて悪手でしょ、という皆様が正しいです。
が、無駄で低品質なコードを掲げていますので、お付き合いください。
壁か目的地か荷物かの情報をもつ構造体配列をモジュールレベルで宣言します。
"m_"がモジュールレベルであることを意味します。
Option Explicit 'セルの状態(構造体) Private Type typPaintInfo HasWALL As Boolean '壁かどうかの情報 HasCRATE As Boolean '荷物が置かれているかの情報 HasGOAL As Boolean '目的地かどうかの情報 End Type 'モジュールレベル構造体 Private m_typCellInfo(C_MIN_ROW To C_MAX_ROW, _ C_MIN_COL To C_MAX_COL) As typPaintInfo
複数が True になるケースとしては荷物かつ目的地という状態だけなので、単に二次元配列にシェイプ名("壁"とか"済"とか)を格納していくほうが楽だと思います。
荷物が動いたときは、この構造体配列を更新していきます。
まず、初期情報を構造体にセットしていくコードです。
マクロ(プロシージャ)名を "sbStatusInfo" とし、作成します。
'---------------------------------------------------------------------- ' sbStatusInfo ' 各セルの情報(壁・荷・的)を構造体にセットする '---------------------------------------------------------------------- Sub sbStatusInfo() Dim intRow As Integer Dim intCol As Integer '初期化 For intRow = C_MIN_ROW To C_MAX_ROW For intCol = C_MIN_COL To C_MAX_COL With m_typCellInfo(intRow, intCol) .HasWALL = False .HasCRATE = False .HasGOAL = False End With Next intCol Next intRow '荷物情報 'E5 m_typCellInfo(C_MIN_ROW + 3, C_MIN_COL + 3).HasCRATE = True 'G6 m_typCellInfo(C_MIN_ROW + 4, C_MIN_COL + 5).HasCRATE = True 'D7 m_typCellInfo(C_MIN_ROW + 5, C_MIN_COL + 2).HasCRATE = True '目的地情報 'F3 m_typCellInfo(C_MIN_ROW + 1, C_MIN_COL + 4).HasGOAL = True 'G3 m_typCellInfo(C_MIN_ROW + 1, C_MIN_COL + 5).HasGOAL = True 'H3 m_typCellInfo(C_MIN_ROW + 1, C_MIN_COL + 6).HasGOAL = True '壁情報 'C2~I2 For intCol = C_MIN_COL + 1 To C_MAX_COL - 1 m_typCellInfo(C_MIN_ROW, intCol).HasWALL = True Next intCol 'C3 m_typCellInfo(C_MIN_ROW + 1, C_MIN_COL + 1).HasWALL = True 'I3 m_typCellInfo(C_MIN_ROW + 1, C_MAX_COL - 1).HasWALL = True 'C4 m_typCellInfo(C_MIN_ROW + 2, C_MIN_COL + 1).HasWALL = True 'G4~J4 For intCol = C_MIN_COL + 5 To C_MAX_COL m_typCellInfo(C_MIN_ROW + 2, intCol).HasWALL = True Next intCol 'B5 m_typCellInfo(C_MIN_ROW + 3, C_MIN_COL).HasWALL = True 'C5 m_typCellInfo(C_MIN_ROW + 3, C_MIN_COL + 1).HasWALL = True 'D5 m_typCellInfo(C_MIN_ROW + 3, C_MIN_COL + 2).HasWALL = True 'J5 m_typCellInfo(C_MIN_ROW + 3, C_MAX_COL).HasWALL = True 'B6 m_typCellInfo(C_MIN_ROW + 4, C_MIN_COL).HasWALL = True 'F6 m_typCellInfo(C_MIN_ROW + 4, C_MIN_COL + 4).HasWALL = True 'H6 m_typCellInfo(C_MIN_ROW + 4, C_MIN_COL + 6).HasWALL = True 'J6 m_typCellInfo(C_MIN_ROW + 4, C_MAX_COL).HasWALL = True 'B7 m_typCellInfo(C_MIN_ROW + 5, C_MIN_COL).HasWALL = True 'F7 m_typCellInfo(C_MIN_ROW + 5, C_MIN_COL + 4).HasWALL = True 'J7 m_typCellInfo(C_MIN_ROW + 5, C_MAX_COL).HasWALL = True 'B8 m_typCellInfo(C_MIN_ROW + 6, C_MIN_COL).HasWALL = True 'F8~J8 For intCol = C_MIN_COL + 4 To C_MAX_COL m_typCellInfo(C_MIN_ROW + 6, intCol).HasWALL = True Next intCol 'B9~F6 For intCol = C_MIN_COL To C_MIN_COL + 4 m_typCellInfo(C_MIN_ROW + 7, intCol).HasWALL = True Next intCol End Sub
ミスがあって、何度か書き直しました。
実際のゲームプログラミングは全然違うんでしょうね。
別シートに設計図を用意して、それをベースにして読み込んだ方が楽だと思います。
次回はシート上に初期状態を展開していきます。
ご質問は下の 「コメントを書く」 からお願いします。
ExcelやVBA全般に関わる質問で、比較的簡単にお答えできるものはできる限り回答したいと思います。
回答を公開でなくメールでやり取りしたいという場合は、その旨記載していただければ非公開で回答することも可能です。
有償での作業依頼は非公開にしますので、条件等をお知らせください。