VBAテトリス 詳細編 テトリミノが落下する
どうもこんにちは。
エクセルのマクロでテトリスを作ってみました。
programminghajimetemita.hatenablog.com
今回はテトリミノが自然に落下する動きをどうやって再現したか、
解説していきたいと思います。
- 1.テトリスの挙動
- 2.繰り返し処理を行うコード
- 3.一行下に壁やブロックがないかチェックするコード
- 4.繰り返し処理を終了するコード
- 5.現在地のテトリミノ消去⇒一行下にテトリミノ表示をするコード
- 6.まとめ
- 7.おわりに
1.テトリスの挙動
処理の内容に入る前に改めてテトリスの挙動について考えてみます。
自然落下する動きを細かく分解すると以下のような動きになっているかと思います。
■スタート時点
・テトリミノがゲーム画面上部に表示されている
■自然落下する動き
・テトリミノの一番下に位置するブロックの一行下に壁やブロックがないかチェック
・なければ現在地のテトリミノを消去
・現在地の一行下にテトリミノを表示
⇒ 消去・表示の動作で一行下に移動したように見える
・移動後、再度、一行下に壁などがないかチェック
・なければ現在地のテトリミノ消去
・一行下にテトリミノ表示
・チェック
・消去
・表示
・・・以下、チェック・消去・表示を壁等に接地するまで繰り返す
この分解した動きを踏まえると以下のような処理を組めばよいということがわかります。
-以下の処理を繰り返す-
・テトリミノの一行下にブロックや壁がないかチェック
■ ブロックや壁がある場合
・繰り返し処理を終了する
■ ブロックや壁がない場合
・現在地のテトリミノを消去する
・現在地の一行下にテトリミノを表示する
あとはこれをコード化すればよいのみです。
動きを細かく考えるとどういう処理をさせればよいのか見えますね。
次からは具体的にどのようなコードを書いたのか解説していきます。
2.繰り返し処理を行うコード
まずは繰り返し処理を行うのに使用するコードです。
-以下の処理を繰り返す- ⇐この部分
・テトリミノの一行下にブロックや壁がないかチェック
■ ブロックや壁がある場合
・繰り返し処理を終了する
■ ブロックや壁がない場合
・現在地のテトリミノを消去する
・現在地の一行下にテトリミノを表示する
繰り返し処理をさせるコードは"For"と"Do"がありますが、
自作したテトリスでは"For"を使いました。
また、"For"の中でも一定回数処理を繰り返すものを使いました。
具体的には以下のようなコードです。
For r = start_r To end_r
・・・繰り返す処理・・・
Next r
※ start_rはゲーム画面上部のテトリミノが始めに登場する位置(行番号)、
end_rはゲーム画面下部の壁(黒色部分)の手前の空白行(行番号)を指します
なぜこのコードを採用したかというと、これを使えば自動的に"r"を1加算することができるので、一行下にテトリミノを移動させる処理と相性が良いと考えたためです。
例えば、以下のような繰り返し処理を考えてみます。
For r X To X
r行目を起点に表示されたテトリミノを消す
r+1行目を起点にテトリミノを表示する
Next
ここで、ゲームスタート時に1行目(r=1)を起点にテトリミノが表示されたとして、
r=1からr=10まで繰り返し処理を行うとすれば以下のようになります。
■スタート時点
・1行目を起点にテトリミノ表示
■繰り返し処理(r=1からr=10まで)
-r=1-
・r行目=1行目を起点に表示されたテトリミノを消去
・r+1行目=2行目を起点にテトリミノ表示
-r=2-
・r行目=2行目を起点に表示されたテトリミノを消去
・r+1行目=3行目を起点にテトリミノを表示
・・・以下繰り返し
具体例を見てわかる通り、「現在地を消去⇒現在地の一行下に表示」を繰り返して下に落ちて行く動きが再現されています。
Forを使わなければ、rを1加算する処理を繰り返し処理の中に組み込む必要がありますが、
Forを使うことでその処理を書かずともよくなるため、コードを書く手間を省くことができます。その点で相性が良いということです。
3.一行下に壁やブロックがないかチェックするコード
これはテトリミノの一行下に壁やブロックがなければ"True"を返す関数を作って対応しました。
なお、ブロックや壁があるセルは背景色が黒色、それらがないセルは背景色が無色であることを考えると、
"一行下に壁やブロックがない"="テトリミノのセル範囲の一行下が無色である"
となるため、
これを利用して関数を作りました。
具体的には以下のような関数です。
Function checkdown(block As Integer, r As Integer, c As Integer) As Boolean
Select Case block
Case 1
If Range(Cells(r + 2, c), Cells(r + 2, c + 2)).Interior.ColorIndex = -4142 Then
checkdown = True
End If
Case 2
・・・
End Select
End Function
この関数には3つの引数を設定しています。
一つは"block"です。これはこの値に応じたテトリミノのセル範囲を呼び出すために用意しています。
残り二つは"r"と"c"です。これはテトリミノのセル範囲を呼び出す座標を与えるために用意しています。
まず始めに"block"の値に応じて条件分岐をさせています。
これは現在地に表示されたテトリミノと同じ形のセル範囲を呼び出すために、
そのように条件分岐をさせています。
次にIfによる条件式を記載していますが、この部分が"テトリミノのセル範囲の一行下が空白か"を確認する部分になっています。
Rangeによるセル範囲がコードで書かれていますが、
具体的にどの部分を指しているかは以下のイメージ図を確認するのがわかりやすいでしょう。
L字型のテトリミノの下段のブロックのちょうど一行下の部分が範囲として指定されています。
この範囲が無色である、という点は"ColorIndex"を利用しました。
無色の場合のColorIndex番号は"-4142"でしたので、上記のようなコードになっているというわけです。
条件式が"True"である、つまり、テトリミノのセル範囲の一行下が無色である場合、
関数checkdownは"True"を返すことになります。
以上、下に進めるかをチェックする関数の紹介でした。
4.繰り返し処理を終了するコード
下に進めるかチェックする関数ができれば、あとはその関数の返す値に応じて条件分岐させ、それぞれの分岐先の処理をくみ上げるのみです。
ここでは、チェックの結果、下に進めない=壁やブロックがある場合の処理を行うためのコードについて触れます。
-以下の処理を繰り返す-
・テトリミノの一行下にブロックや壁がないかチェック
■ ブロックや壁がある場合
・繰り返し処理を終了する ⇐この部分
■ ブロックや壁がない場合
・現在地のテトリミノを消去する
・現在地の一行下にテトリミノを表示する
ブロックや壁がある場合はそれ以上下に進むことはできないため、
繰り返し処理を終了させる必要があります。
繰り返し処理を終了させるためのコードは簡単で以下のような記載になります。
Exit For
5.現在地のテトリミノ消去⇒一行下にテトリミノ表示をするコード
最後に、テトリミノが下に移動(=現在地を消去、一行下に表示)する挙動を再現するコードです。
これは以下のようなコードを書きました。
-本体-
Call clearblock(block, r, c)・・・テトリミノを消去するマクロを呼び出す
Call createblock(block, r + 1, c)・・・テトリミノを表示するマクロを呼び出す
-テトリミノを消去するマクロ-
Sub clearblock(block As Integer,r As Integer, c As Integer)
Select Case block
Case 1
Set block1=Union(Cells(r, c), Range(Cells(r + 1, c), Cells(r + 1, c + 2)))
block1.ClearFormats
・・・以下他の種類のテトリミノについても同様のコードを記載
-テトリミノを表示するマクロー
Sub createblock(block As Integer,r As Integer, c As Integer)
Select Case block
Case 1
Set block1=Union(Cells(r, c), Range(Cells(r + 1, c), Cells(r + 1, c + 2)))
With block1
.Interior.ColorIndex = 1
.Borders.LineStyle = xlDouble
.Borders.ColorIndex = 2
End With
・・・以下他の種類のテトリミノについても同様のコードを記載
ポイントをかいつまんで説明します。
まず、やりたい処理は"現在地のテトリミノ消去"、"現在地の一行下にテトリミノ表示"ですので、
消去するマクロ、表示するマクロの順番で呼び出しているというわけです。
また、テトリミノの位置は座標(r, c)によって定まるため、
現在地のテトリミノを消去するときは(r, c)をそのまま引渡し、
現在地の一行下にテトリミノを表示するときは行番号のみ調整して、
(r + 1, c)の座標を引き渡しています。
テトリミノの消去については、テトリミノが表示されているセル範囲に対して、
"ClearFormats(書式のクリア)"を行うことで再現しています。
テトリミノの表示については、テトリミノのセル範囲に対して、
書式を付加(上から順に、背景色:黒色、二重罫線を引く、罫線色:白色)することで再現しています。
なお、消去をテトリミノの表示範囲と一致させているのは、
壁や他のブロックに影響を与えないためです。
単に消すだけならテトリミノの範囲に囚われずに消せばよいですが、
先に積まれていたブロックや壁が消えてしまってはテトリスでなくなってしまうため、
そのような事態を起こさないようにするために一致させています。
6.まとめ
ここまで各処理にわけてどのようなコードを書いたか解説してきました。
解説したコードをまとめると以下のようになります。
・・・太字で示している部分は上述のセクションと対応しています。
まとめパートから細かい部分について確認したい場合は各セクションをご参照下さい。
なお、ここまでで触れていないコードとして"Application.Wait"というコードがあります。
テトリミノを消去するマクロ(Call clearblock)とテトリミノを表示するマクロ(Call createblock)の間に入っているコードです。
これは指定の秒数マクロの処理を中断させるコードです。
このコードを実行することにより、
上段の方は0.1秒間、下段の方は1秒間の待機時間が生じます。
なぜこのコードを入れているかというと、
待機時間を設けなければもの凄いスピードでテトリミノが落ちてしまうためです。
落ちるスピードが早すぎると当然ゲームになりませんから、
これによってスピードを調整しているわけです。
以上、かなり長くなってしまいましたが、テトリミノが落下するコードの解説について終わりたいと思います。
7.おわりに
今回もかなり内容が長くなってしまいました。
説明が冗長になってわかりづらい部分もあるかもしれませんが、ご了承ください。
また、プログラミング初心者の私が書いたコードですので、
書き方がイケてない部分も多々あるかと思いますが、
そちらもご了承いただければと思います。
次回はテトリミノを操作する部分のコードについて解説します。
ではまた。