文系人間がプログラミングをはじめてみた。

プログラミング初心者のゼロからの勉強記録。

VBAテトリス 詳細編 ブロックの消去を演出する

どうもこんにちは。

 

今回もエクセルのマクロでつくったテトリスの解説をしていきます。

今回はブロック消去処理の番外編です。

単に消去するだけでなく、実機のように消去に演出を加えるためのコードの解説になります。

 

テトリスのイメージはこちらから確認できますので興味があればどうぞ。

programminghajimetemita.hatenablog.com

 

では解説に入ります。

 

 

1.ブロック消去処理の振り返り

前回の記事で解説した通り、

ブロックを消去する処理はセルの書式を調整することで行っています。

前回記事の内容を見たい方は以下リンクからどうぞ。

programminghajimetemita.hatenablog.com

 

ということで、消去のためにセルの書式を調整するコードに工夫を加えれば、

一風変わった演出にできるというわけです。

 

では、どのように手を加えたのか次から解説していきたいと思います。

 

2.テトリスの挙動

工夫を加えるためにも、まずは本家の動きがどうなっているのかを把握することが重要です。

ということで、まずは本家の挙動について考えていきます。

 

私はこのマクロのテトリスを作る際にテトリスミニの挙動を参考にしましたので、

以下紹介する挙動はテトリスミニの挙動になります。

 

どんな演出がなされるかというと、

・ブロックが揃った行すべてが数回点滅する

という演出です。

 

この挙動を見て、消去行すべてを同時に点滅させるのは難しいなと思い諦めました。笑

諦めも時に肝心です。

 

ただ、何かしら演出は加えたいという思いがありましたので、

実際に作ったマクロでは、

「ブロックの揃った行について、左から一つずつブロックが消えていく」

という処理を入れました。

 

この演出でも、"この行が消えるんだ!"というのが明確になるので、

まずまずの演出ができているのではないかと自負しています。

 

では、次のセクションで具体的なコードの内容に入っていきます。

 

3.ブロックを消去するコード

繰り返しになりますが、処理内容は以下のとおりです。

「ブロックの揃った行について、左から一つずつブロックが消えていく」

 

上記処理のために実際に作ったコードの内容は以下の通りです。

なお、細字部分は前回記事で紹介したコードと同じコードです。

 

    For r = end_r To start_r Step -1
        If checklineblack(r) = True Then
            For c = leftedge_c To rightedge_c
                With Cells(r, c)
                    .Interior.ColorIndex = 2
                    .Borders.LineStyle = xlDouble
                    .Borders.ColorIndex = 1
                End With
                Application.Wait [Now() + TimeValue("00:00:00.03")]
            Next c
        End If
    Next r

※ leftedge_cはゲーム画面の左端列、rightedge_cはゲーム画面の右端列を指す定数

 

コードのポイントについて解説します。

 

記述しているコードはかなり単純です。

ブロックを一つずつ消去するということで、

繰り返し処理を利用して一セルずつ書式の変更を行っているだけです。

 

繰り返し処理にはおなじみForを使っており、

繰り返しの回数はゲーム画面内の一行全体を消去するわけですから、

ゲーム画面の左端から右端までになるということです。

 

消去を再現する書式の調整については前回記事で解説したのと同じコードです。

 

最後に、Application.Waitというコードがありますが、

これは実際に処理を実行したときに、

人の目で見て滑らかに映るようにするために、

処理時間の調整として付加したコードです。

何も入れないと、ぱっと処理が終わってしまい味気ないので、

あえて処理中断時間を入れることでゲームっぽい演出にしているわけです。

 

以上でコードの解説を終わります。

 

4.おわりに

今回はブロック消去を演出するコードについて解説しました。

ここまででマクロテトリスのコードの大部分の解説が終わりました。

これらのコードを実際に組み、つなぎ合わせると、

かなりテトリスっぽい挙動になっているかと思います。

 

完成まで残りもう少しですので、

もうしばらくお付き合いください。

 

次回はブロックの消去に応じてSCOREなどを計算する処理について解説します。

ではまた。

VBAテトリス 詳細編 ブロックを消去する

どうもこんばんは。

 

エクセルのマクロで作ったテトリスの解説記事です。

今回は「揃ったブロックを消去する処理」について解説していきます。

 

どんな感じのテトリスになっているかはこちらからご参照ください。

programminghajimetemita.hatenablog.com

 

では内容に入ります。

 

 

1.テトリスの挙動

まずはおなじみのテトリスの挙動から考えてみましょう。

ブロックを消去するときの挙動です。

 

テトリスでは、ブロックが揃う(一行全体にブロックが埋まる)と、

揃ったタイミングで、その行が消去されます。

また、行が消去された後、未消去の行が下に落ちます。

 

かなりざっくりした説明ですが、イメージつかめたでしょうか?

では、ここからもう少し細かくかみ砕いて、

どういった処理が必要になるのか考えていきます。

 

まず、「ブロックが揃うと、消去する動作が行われる」ので、

これを行うためには、「ブロックが揃った行があるかどうかをチェックする」処理が必要になりそうです。

 

また、ブロックが揃うのは一行のときがあれば、複数行のときもありますし、

行が連続する場合があれば、行がとんで揃う場合もあります。

チェックにあたっては、こういったケースをまとめて同時に「揃った行である」、

と判定できる必要があります。

 

次に、判定された行を消去する処理です。

"消去"をもう少し具体的に表現すると、

ゲーム画面上、ブロックが表示されていた部分から、

ブロックがなくなったように見えること、と言えます。

 

ここで、テトリスのマクロでは、

ブロックはセルの書式を調整することで表示しています。

ですので、"ブロックがなくなったように見える"についても

セルの書式を調整することで実現できそうです。

 

したがって、ブロックの消去を再現するためには、

ブロックの揃った行の書式を調整する処理を行えばよいですね。

なお、今回作成したテトリスでは消去の書式は以下のように設定しました。

・背景色:白色

・二重罫線を引く

・罫線色:黒色

 

ちょうど、ブロックの書式を反転させた書式になっています。

この書式がテトリスミニの消去時の表現と似ていたので、このようにしました。

どういった書式を設定するかは自分好みにカスタマイズください。

 

最後に、未消去の行を下に落とす処理です。

これはどういう処理をさせればよいのか、思いつくまでに割と時間がかかりました。

 

具体例を見て考えてみましょう。

以下のイメージは未消去の行が下に落ちる前と落ちた後の絵を並べたものです。

ここから未消去の行を落とす処理には2つ特徴があることがわかります。

1つは消去された行よりも下にある行には変化がないということです。

上のイメージでいえば6行目と7行目です。

 

もう1つは1行目から消去行の1行上までの内容が、

2行目から消去行までにコピペされているということです。

2上のイメージでいえば5行目が消去されて、

1行目から4行目の内容が2行目から5行目にそっくりそのまま移ってますね。

 

よって、未消去の行を下に落とすためには、消去行を仮に"r"とすると、

1行目から"r - 1"行目の内容を2行目から"r"行目にコピペする処理を行ってやればよいです。

 

上のイメージは1行だけ消去される場合のものでした。

テトリスでは複数行が消去される場合もありますので、

その場合についても同じように考えてみましょう。

 

上のイメージは連続した行が消去されるパターン、

下のイメージは行がとんで消去されるパターンになります。

 

消去行が連続していれば一度のコピペでなんとかなりそうですが、

行がとんでいる場合だとうまくいきそうにないですね。

 

一度の処理でうまくいかないのであれば、

処理を繰り返せばよいのではないか、と考えて、

繰り返し処理を使って、どんなパターンでもうまく処理される方法を考えました。

 

で、思いついた処理が、消去行がなくなるまでコピペを繰り返す処理です。

言葉ではわかりづらいと思いますので、以下のイメージをご覧ください。

 

上のイメージ(消去行が連続している)では、

まず、下から数えて一つ目の消去行(5行目)を基準に、

単一の消去行のときの処理を行っています。

つまり、1行目から4行目の内容を2行目から5行目にコピペする処理です。

コピペ後の結果が、真ん中の図になります。

 

このときイメージを見てわかる通り消去行がまだ残っています。

消去行も含めてコピペしたので当然ですね。

ということで、この消去行についても同じ処理を繰り返します。

再度のコピペ後の結果が、右端の図になります。

最初に例に挙げたときと同じ結果になっているので見比べてみてください。

 

下のイメージ(消去行が飛んでいる)も同様の処理でうまく処理することが可能です。

まずは下から数えて一つ目の消去行を基準に処理を行います。

1行目から4行目の内容を2行目から5行目にコピペです。

 

こちらも消去行が残っていますので、残った消去行について処理をかけていきます。

消去行が4行目に来ていますので、1行目から3行目の内容を2行目から4行目にコピペですね。

こちらも、コピペ後のイメージ(右端図)が例に挙げたときと同じ結果になっていると思います。

 

というわけで、消去行が複数あってそれがどんなパターンになっていようとも、

常に、下から数えて一つ目の消去行(r行目と仮定します)を基準に、

1行目から"r - 1"行目の内容を2行目から"r"行目にコピペし、

それを消去行がなくなるまで繰り返せば、うまくいくことがわかりました。

 

長くなりましたが、ここまでのまとめです。

 

問題:ブロックを消去する挙動を再現するためにどのような処理が必要か?

答え:

・ゲーム画面内にブロックが揃っている行があるかチェックする

・ブロックが揃っている行を消去(セルの書式の調整)する

・下から数えて一つ目の消去行について、

1行目から消去行の1行上の内容を2行目から消去行までコピペする

・3点目の動作を消去行がなくなるまで繰り返す

 

さあ、ようやくどのような処理をさせればよいかが具体化できましたので、

それらを行うのにどのようなコードを組めばよいか、次から解説していきます。

 

2.ブロックが揃っている行があるかチェックするコード

一つ目はブロックが揃っている行があるかチェックするコードです。

 

これは、ゲーム画面内のすべての行を対象に、1行ずつ、

ブロックが揃っている行があるかチェックを繰り返すコードを作りました。

 

繰り返し処理はおなじみの"For"を使っています。

 

ブロックのチェックについては関数を自作しました。

チェック対象の行が、ブロックの揃った行であれば"True"を返す関数です。

 

「ブロックの揃った行である」をどう判定しているか、についてですが、

ブロックがあるということは、そのセルの背景色が黒色であると同義ですので、

ブロックが揃っているというのは、一行すべて背景色が黒色であると言えます。

 

ということで、関数の内容は、

チェック対象の行の背景色が黒色であれば"True"を返す、

というものになります。

 

実際のコードは以下のとおりです。

 

    For r = end_r To start_r Step -1
        If checklineblack(r) = True Then

・・・(ブロックが揃った行を消去する処理)・・・

        End If
    Next r

start_rはゲーム画面の1行目、end_rはゲーム画面の最下行を指す定数

 

ー関数(上記のコードの赤字部分)の内容-

Function checklineblack(r As Integer) As Boolean
        If Range(Cells(r, leftedge_c), Cells(r, rightedge_c)).Interior.ColorIndex = 1 Then
            checklineblack = True
        End If
End Function

leftedge_cはゲーム画面の左端列、rightedge_cはゲーム画面の右端列を指す定数

 

上記コードの補足です。

繰り返し処理の中で、"Step -1"というコードが出てきます。

これはカウント変数(上記でいえば"r")を1ずつ減らしていくよう処理させるのに必要なコードです。

rは最下行から1行目に向かって数値を変えて欲しいので、

このような処理が必要になるというわけです。

 

なお、1行目から最下行に向けて処理しても同じような結果になりますが、

ゲーム上、ブロックは下の行から積みあがっていきますので、

先に積みあがった方(つまり下の行)から先に処理していった方が、

見栄えがよいだろうということでこのような処理にしています。

 

3.ブロックを消去するコード

続いてブロックを消去するコードです。

先ほどの「ブロックの揃っている行があるかチェックするコード」を見てわかるとおり、

ブロックを消去するコードは上記コードの続きで記述するコードになっています。

 

    For r = end_r To start_r Step -1

        If checklineblack(r) = True Then

・・・(ブロックが揃った行を消去する処理)・・・

        End If

 Next r

 

消去は、最初に解説したとおり、セルの書式を調整することで行いますので、

ブロックが揃った行の書式を変えればOKですね。

ということで、以下コードです。

 

    For r = end_r To start_r Step -1

        If checklineblack(r) = True Then

                With Range(Cells(r, leftedge_c), Cells(r, rightedge_c))
                    .Interior.ColorIndex = 2    (背景色:白色)
                    .Borders.LineStyle = xlDouble (二重罫線を引く)
                    .Borders.ColorIndex = 1    (罫線色:黒色)
                End With

        End If

 Next r

 

なお、実際のコードは上記と異なります。

というのも、自作したテトリスの概要でお話ししたのですが、

消去にちょっとした演出を加えているためです。

(下図はテトリスを再現するのに追加した要素サマリの再掲)


今回紹介したコードでは1行全体の書式が一瞬で変わるので、

頑張って積み上げてきたブロックを消去する演出として少し味気ないです。

そのため、少しコードに手を加えて、

より実機に近い(かどうかは微妙ですが)演出になるよう工夫を行っています。

 

そちらのコードの内容は後日改めて解説します。

 

4.未消去の行を下に落とすコード

最後に未消去の行を下に落とすコードです。

 

最初に解説したとおり、未消去の行を下に落とすために必要な処理は以下2つです。

・下から数えて一つ目の消去行について、

1行目から消去行の1行上の内容を2行目から消去行までコピペする

・上記の動作を消去行がなくなるまで繰り返す

 

ざっくりいえば、コピペを繰り返す、ということですね。

処理は具体化できているので早速コードの内容に入ります。

 

    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    For r = end_r To start_r Step -1
        If checklinewhite(r) = True Then
            Range(Cells(start_r, leftedge_c), Cells(r - 1, rightedge_c))

   .Copy Destination:=

   Range(Cells(start_r + 1, leftedge_c), Cells(r, rightedge_c))
            r = r + 1
        End If
    Next r

 

ではポイントを解説します。

最初の2行はひとまず置いておいて、繰り返し処理から解説します。

これはブロックの揃った行のチェックのときと同様に、

最下行から1行目に向かって処理させるため、

"Step -1(カウント変数を1ずつ減らす)"というコードを入れています。

 

・・・ブロックの揃った行のチェックと平仄を合わせてそのようにしましたが、

1行目から繰り返し処理を行う(つまり、r=start_r To end_rとする)のでも同じ結果になるかもしれません。

しかもその方がコードの行数を減らせるような気がします。

興味のある方はぜひ試してみてください。

 

次に、赤字のコードcheckwhiteline(r)についてです。

これはチェック対象の行が消去された行に該当するかをチェックする関数です。

 

やっていることはブロックの揃った行のチェックと同じです。

消去された行は一行全体の背景色が白色になっている(上記で解説したコードをご参照ください)ので、

一行全体の背景色が白色になっているかを上記関数でチェックしています。

コードの内容はcheckblackline(r)とほぼ同じですので割愛しています。

(ColorIndex=1がColorIndex=2になるだけです)

 

続いてコピペのコードです。

コピペのコードは、

(コピーしたい範囲).Copy Destination:=(ペースト先の範囲)

のように記述すればOKです。

 

コピペの範囲については、最初の方で触れたとおり、

「1行目から消去行の1行上」の内容を「2行目から消去行」にコピペするわけですから、

上で記述しているようなコードになります。

 

なお、コピペ後に「r = r + 1」という式が入っています。

これは消去行に対して漏れなく処理するために必要な処理になります。

どういうことか具体例で説明します。

 

仮に、4行目・5行目を消去したとします。

処理は下にある行から始まりますので、まず5行目の消去行から処理されることになります。

処理の結果、1~4行目が2~5行目にコピペされます。

これによって4行目にあった消去行は5行目に移動してしまっています。

 

ここで、繰り返しのカウント変数を何も調整しない場合、

r = 5のときの処理が終わると、次にr = 4のときの処理に移ります。

 

そうなると、コピペの結果、当初4行目にあった消去行は5行目に移動していますから、

当該行が処理されないまま繰り返し処理が終わってしまうことになります。

 

ということで、こういった消去行の処理漏れを防ぐために、

"r = r + 1"の処理が必要になるわけです。

 

最後に、解説を飛ばした最初の2行について説明します。

これらはいずれもエクセルの自動処理を止めるコードです。

(1行目は画面の更新、2行目は数式の再計算)

 

これらを止めることによって、マクロの処理速度が向上するので、

コピペ処理を行う前に記述しています。

 

ブロックを揃えた後、それを消去するまでに長い時間がかかってしまうとゲームっぽくないですからね。

特にコピペは処理に時間がかかりますので、

少しでも早く処理するためにこのような工夫を行っています。

 

以上でブロックを消去する処理のコード解説を終わります。

 

5.おわりに

今回はブロックを消去する処理について解説しました。

かなりボリュームが多く、見づらいかもしれませんがご容赦くださいませ。

 

次回はライトなボリュームの記事にするよう気を付けます。

ではまた。

VBAテトリス 詳細編 テトリミノを操作する2

どうもこんにちは。

 

マクロで作ったテトリスの解説記事です。

今回は前回の内容の続きで「テトリミノを操作する」処理についてです。

前回の内容は以下リンクからご参照ください。

programminghajimetemita.hatenablog.com

 

前回の記事では、テトリミノを移動させたり、回転させる処理を行うコードについて解説しました。

ただ、紹介したコードではブロックや壁を無視して移動や回転ができてしまうので、

移動や回転ができる範囲を制限する必要があります。

 

では、移動や回転ができる範囲を制限するにはどういったコードを準備すればよいでしょうか?

今回の記事では、そのあたりを中心に解説していきたいと思います。

 

 

1.テトリミノが落下する処理を振り返る

テトリミノの移動等を制限する処理ですが、実は以前にその内容を紹介しています。

いつ紹介したかというと、テトリミノが落下する処理を紹介したときです。

テトリミノを落下させるにあたり、あらかじめ下に進めるかどうかをチェックする必要があり、

そのためにどのようなコードが必要かを解説しました。

その時の記事はこちらです。

programminghajimetemita.hatenablog.com

 

具体的にどのような処理を行ったかと言いますと、

"テトリミノの一行下の範囲が無色になっているか"、を判定する処理を行いました。

以下のイメージの赤色の範囲が無色かどうかチェックするということです。

 

移動や回転に関しても、上記と同様に、

移動先や回転によってブロックが移動する先が空白になっているか、

をチェックしてやればOKです。

 

では具体的なコードの内容に入っていきましょう。

 

2.テトリミノが移動できるか判定するコード

まずは移動(左・右)に関するコードです。

下移動の時と同様に、無色かどうかをチェックするセル範囲について考えてみましょう。

以下、左移動に関してのイメージです。

こんな感じで、すべてのテトリミノについて、無色かどうか判定すべき範囲をリストアップしていきます。

また、テトリミノの種類だけでなく、回転によっても無色かどうか判定すべき範囲が変わってきますから、

すべての回転後の図形の種類についてもリストアップが必要です。

 

まとめると、以下の全図形について無色かどうか判定すべきセル範囲をリストアップするということです。

 

たくさんありますね。作業は単純なので頑張りましょう。

根気があればできます。

 

実際に作成したコードの一部をご紹介します。

 

Function checkleft(block As Integer, r As Integer, c As Integer, rotate_num As Integer) As Boolean
    Select Case rotate_num
        Case 0
            Select Case block
                Case 1, 7
                    If Range(Cells(r, c - 1), Cells(r + 1, c - 1)).Interior.ColorIndex = -4142 Then
                        checkleft = True
                    End If

   ・・・以下、回転後の図形の種類(rotate_num)、

      およびテトリミノの種類(block)ごとに上記と同様の記述を行う

 

 

これで、左に移動できる(左側が無色である=ブロックや壁がない)時に"True"を返す関数が完成しました。

これを左移動のコードに組み込めばOKです。

具体的には以下の通りです。

 

    If checkleft(block, r, c, rotate_num) = True Then
        If GetAsyncKeyState(vbKeyLeft) <> 0 Then
            c = c - 1
        End If
    End If

 

太字部分が今回新たに追加した内容、細字部分が前回紹介した内容になります。

これでブロックや壁がない箇所にのみ左移動ができるようになりました。

 

右移動に関しても左移動と同様に、

・無色であるか判定すべきセル範囲をリストアップ

・その範囲が無色である場合にTrueを返す関数を作成

・右移動の条件として当該関数=Trueであることを追加

すれば、移動範囲に制限をかけることができます。

 

以上で移動可能かの判定に関するコードの紹介を終わります。

 

3.テトリミノが回転できるかを判定するコード

続いて回転できるかを判定するコードです。

考え方は移動可能かの判定と同様です。

ポイントになるのは、「どの範囲を判定対象とすべきか」ということです。

 

移動の場合は表示する位置が変わるのみで、表示する図形自体は変わりませんでした。

ですので、どこを判定対象とすべきかわかりやすかったかと思います。

 

回転の場合は表示する位置に変更はありませんが、表示する図形に変更があります。

ですので、回転後の図形を踏まえて判定対象とすべき範囲を考える必要があります。

 

具体的なイメージは以下の通りです。

 

回転前の図形が赤点線部分、回転後の図形が黒色部分になります。

そして、無色と判定すべき範囲が赤色の枠で囲っている部分になります。

 

回転前の図形と回転後の図形で表示位置が一行ずれているのは、

回転するとともに自然落下(下移動)も行われますので、

それを加味しているためです。

 

イメージ図を見てわかるように、

回転前の図形から回転後の図形にするためには、

回転前の図形からみて、

回転後にブロックが来るであろう位置が無色である必要があります。

ということで、上の例では赤枠部分になるわけです。

 

こちらも移動可否の判定の時と同じように、

すべてのテトリミノの種類、すべての回転時の図形の種類ごとに、

無色と判定すべき範囲をリストアップが必要です。

 

また、上で説明した通り、回転前の図形から回転後の図形になるのを踏まえて、

無色と判定すべき範囲を考えなければなりません。

 

・・・かなり面倒ですね。より根気のいる作業です。

ただ、根気があればそんなに深い知識がなくてもできますので、

初心者にやさしい(?)コードだと思います。

 

具体的なコードの内容は、

移動可否の判定のものとほぼ同じ(無色と判定すべき範囲が違うのみ)ですので、

割愛します。

 

以上で回転可否の判定に関するコードの解説を終わります。

 

4.おわりに

今回はテトリミノを移動・回転させるにあたり、

移動・回転が可能な範囲を制限するのに必要なコードについて解説しました。

 

根気のいる作業と言った通り、コード化するとかなりの量のコードになります。

工夫すればもっとシンプルなコードになるのかもしれませんが、

初心者が作ったコードですのでご容赦ください。

 

ただ、実際にマクロを実行したときにはちゃんと動作します。

結果よければ見た目悪くてもよし、ということで。笑

 

ここまでの内容でかなりテトリスっぽくなってきましたね。

次回からはブロックが揃ったときの挙動を再現するために必要なコードを解説していきます。

 

ではまた。

VBAテトリス 詳細編 テトリミノを操作する

どうもこんにちは。

 

エクセルのマクロでこんな感じのテトリスを作りました。

programminghajimetemita.hatenablog.com

 

このテトリスのコードをどうやって作ったか解説していきます。

今回は「テトリミノを操作する」処理についてです。

 

 

1.テトリミノを操作するとは?

具体的なコードの話に入る前に、

「そもそもテトリミノを操作するって何のことを言っているのか」

についてお話しします。

 

操作するという言葉でイメージがついているかもしれませんが、

「テトリミノを操作する」とは、テトリミノの落下中に、

・テトリミノを左に移動させる

・テトリミノを右に移動させる

・テトリミノを下に移動させる

・テトリミノを回転させる

の4つの動作をユーザーのボタン操作で行うことを指すと捉えてください。

ざっくり言えば、テトリミノの移動と回転ですね。

 

では、これらの処理を行うためにどんなコードを組む必要があるか、

次の章から解説していきたいと思います。

 

2.テトリミノの操作-任意の位置に移動させる

まずはテトリミノの移動の処理から解説していきたいと思います。

コードの内容に入る前にテトリスの挙動をおさらいします。

 

テトリスでは、テトリミノの落下中に左ボタンを押すとテトリミノが左に移動、

右ボタンを押すとテトリミノが右に移動、

下ボタンを押すとテトリミノが素早く落下すると思います。

 

当たり前の挙動ですが、これを考えることでどういう処理をさせればよいかが見えてきますので、

少し冗長かもしれませんがお付き合いください。

 

ということで挙動を踏まえると必要な処理は以下になります。

・左ボタンを押したらテトリミノを左に1列移動させる

・右ボタンを押したらテトリミノを右に1列移動させる

・下ボタンを押したらテトリミノを下により素早く移動させる(=下に移動する行数を増やす)

 

では、この処理を実現するコードの内容に入っていきましょう。

 

3.テトリミノの移動を処理するコード

まずは簡単な部分からコードの内容に触れていきます。

左に移動する処理から考えてみましょう。

 

上述の通り、やりたい処理は「左ボタンを押したら左に1列移動」です。

これはIfによる条件分岐を使えば実現できそうです。

 

If (左ボタンが押された) Then

  c = c - 1

End If

 

これまでに解説してきた通り、テトリミノの位置は変数(r, c)によって制御されています。

ここで、cの値は何も変更が加えられていなければ、初期位置の列になっています。

ですので、左方向に1列移動させたい場合はcに"c - 1(現在地の列の左隣列)"を代入してやればよいというわけです。

 

他の場合も同様で、移動させたい方向に変数の値を調整してやればOKです。

右移動であればcに"c + 1"を代入、

下移動であればrに"r + X(Xはどんな数字でも可)"を代入してやればよいです。

(下方向の移動は行方向の移動になりますから、調整する変数はrになります。

また、"素早く落下"なのでXの数字を大きくすればするほど落下スピードは増します。

これは自分の匙加減によると思いますので挙動を見ながら好きな数字を入れましょう。)

 

あとは上記のコードの中でコード化ができていなかった、

(左ボタンが押された)をどのようなコードにするかです。

 

これはVBAの枠外の力を借りて行います。

具体的にはGetAsyncKeyStateという関数を利用します。

 

この関数を利用すると、

"キーボード上のボタンの現在の状態(押されているかどうか)"

を取得することができます。

 

現在の状態は数字で表され、

ボタンが押されていないのであれば"0"が返されます。

(押された状態は複数の種類があるのですが、細かいので割愛します)

 

なので、この関数が実行されたときに、ボタンが押されていなければ、

"0"という数値が返ってくるということになります。

 

ここで、今処理に組み込みたいのは(ボタンが押された)という条件です。

上記の通り、押されていない:関数=0 ですので、

押された:関数=0でない、つまり、関数<>0という式にすればよいですね。

 

ということで、最終的なコードは以下のようになります。

 

If  GetAsyncKeyState(vbKeyLeft) <> 0 Then

  c = c - 1

End If

 

上記は左ボタンの例ですが、別のボタンで同様のことをやりたいときは、

GetAsyncKeyState関数のカッコ書きの部分を書き換えればOKです。

右ボタンなら(vbKeyRight)、下ボタンなら(vbKeyDown)といった具合です。

 

なお、この関数は最初にVBAの枠外の力といった通り、VBAに規定されている関数ではないので、

利用するにあたっては事前準備が必要になります。

 

具体的には、コードの一番上の行に以下の記述を行う必要があります。

この関数を利用するモジュールそれぞれで記述が必要になりますのでご留意ください。

 

#If VBA7 Then
'32bit PC
Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Long) As Long
#Else
'64bit PC
Private Declare PtrSafe Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As LongPtr) As Long
#End If

 

以上でテトリミノの移動についての解説を終わります。

 

4.テトリミノの操作-テトリミノを回転させる

続いてテトリミノの回転の処理です。

ここでもまずはテトリスの挙動を考えます。

 

テトリスでは回転に対応するボタンを押すと、

表示されているテトリミノが90度(テトリミノの種類によっては180度)回転します。

 

ここで"回転"についてもう少し深堀して考えてみます。

ゲーム画面上、ボタンを押すと表示されているテトリミノがぐるりと回ったように見えますが、

これはボタンを押すと、現在表示されているテトリミノが消えて、

90度回転した形のテトリミノが表示される、とも考えることができます。

これで、どのような処理をさせればよいかがはっきりしましたね。

 

以下が回転を再現するのに必要な処理です。

・任意のボタンを押したら90度(あるいは180度)回転した形のテトリミノを表示する

 

では具体的なコードの内容に入っていきましょう。

 

5.テトリミノの回転を処理するコード

回転に必要な処理は、上述の通り、

90度回転した形のテトリミノを表示すること、です。

また、常に、ボタンを押す前に表示されているテトリミノの形から、

90度回転した形にする必要があります。

 

これを実現するために以下2つの処理を準備しました。

・回転後の図形ごとに番号を割り振る

・ボタンを押すと上記の番号が切り替わる

順に詳しく解説してきます。

 

・回転後の図形ごとに番号を割り振る

これはテトリミノの種類ごとに番号を割り振ったのと似た発想です。

取り得る形を書き出して、それぞれに番号を割り振りました。

具体的には以下のようなイメージです。

("block"がテトリミノの種類を示す番号、"rotate_num"が回転後の図形の種類を示す番号になります)

 

あとは、blockやrotate_numに対応する形を呼び出せるようにしておけばよい、というわけです。

具体的なコードは以下の内容になります。

-テトリミノのセル範囲を設定するマクロー

※block1はRange型のオブジェクト変数

Sub setblock1(r As Integer, c As Integer, rotate_num As Integer)

  Select Case rotate_num

  Case 0

   Set block1 = Union(Cells(r, c), Range(Cells(r + 1, c), Cells(r + 1, c + 2)))
        Case 1
            Set block1 = Union(Range(Cells(r, c), Cells(r, c + 1)), Range(Cells(r + 1, c), Cells(r + 2, c)))
        Case 2
            Set block1 = Union(Range(Cells(r, c), Cells(r, c + 2)), Cells(r + 1, c + 2))
        Case 3
            Set block1 = Union(Cells(r + 2, c), Range(Cells(r, c + 1), Cells(r + 2, c + 1)))
    End Select
End Sub

・・・以下テトリミノ種類ごとに同様の記述を行う

 

ーテトリミノを表示するマクロー

Sub createblock(block As Integer, r As Integer, c As Integer, rotate_num As Integer)
    Select Case block
        Case 1
            Call setblock1(r, c, rotate_num)
            With block1
                .Interior.ColorIndex = 1
                .Borders.LineStyle = xlDouble
                .Borders.ColorIndex = 2
            End With

・・・以下テトリミノの種類ごとに同様の記述を行う

 

コードのポイントについて解説します。

まず、テトリミノのセル範囲を設定するマクロについてです。

 

上記で示しているコードは先ほどお見せしたイメージ図の一行目、

L字型ブロックのセル範囲を設定するコードになります。

 

回転後の図形に割り振った番号ごとに場合分けをし、

それぞれの図形に対応するセル範囲を設定しています。

 

このマクロを実行する際には、r, c, rotate_numの3つの変数が連携されます。

変数r, cによってどの座標を設定するか、変数rotate_numによってどの図形を表示するかが決定されるということです。

 

次にテトリミノを表示するマクロについてです。

こちらは以前解説したマクロになりますが、以前解説した時には図形の回転を無視した内容でしたので、

改めてコードを紹介しました。

 

以前紹介したコードと異なる点は、引数にrotate_numが追加されていること、

それぞれのケースにおいてテトリミノのセル範囲を設定するマクロを呼び出していること、

の2点です。

 

どういう処理をしているかというと、

まずは変数blockによってどの種類のテトリミノを呼び出すかを決定しています。

次に、変数rotate_numによってテトリミノのうちのどの図形を呼び出すかが決まり、

変数r, cによって呼び出す座標が決まります。

呼び出す形、座標が決まれば、あとはその範囲に書式を付加して処理終了となります。

 

まとめると、rotate_numという要素を加えることで回転状態に応じた適切な図形が表示されるようになったということです。

 

・ボタンを押すと回転状態を示す番号(rotate_num)が切り替わる

ここまでで回転の状態に合わせた適切な図形が表示されるようになりました。

あとは回転の状態をボタン押下によって制御できればよいですね。

 

「ボタンを押す」の部分については、

テトリミノの移動の時と同様にGetAsyncKeyState関数を利用します。

 

「番号が切り替わる」の部分については、

ボタンを押す度に、0→1→2→3→0・・・と切り替わっていけばよいので、

 

ボタンが押されたときに、

 ・rotate_numが0、ならば、rotate_numは1になる

 ・rotate_numが1、ならば、rotate_numは2になる

 ・rotate_numが2、ならば、rotate_numは3になる

 ・rotate_numが3、ならば、rotate_numは0になる

 

と処理させればよいですね。

 

ということでコードは以下のようになります。

 

If GetAsyncKeyState(vbKeyControl) <> 0 Then
  If rotate_num = 0 Then
    rotate_num = 1
       ElseIf rotate_num = 1 Then
               rotate_num = 2
       ElseIf rotate_num = 2 Then
               rotate_num = 3
       Else
               rotate_num = 0
       End If

End If

 

番号の切り替え部分は以下のようなコードでもいいかもです。

 

If rotate_num <> 3 Then

  rotate_num = rotate_num + 1

Else

  rotate_num = 0

End If

 

紹介していて冗長に感じたので改善案もついでに紹介してみました。

こんな感じでイケてないコードが多々あるかと思いますので、

やりたい処理をつかんでいただけたら、

ここで紹介しているコードを使わず、

よりシュッと表現できるコードを使ってください。

 

以上でテトリミノを回転させる処理のコード解説を終わります。

 

6.おわりに

今回はテトリミノの操作について解説しました。

この処理を組み込むことで移動や回転が行えるようになるので、

興味があればぜひご自分のPCで試してみてください。

 

実は今回紹介した内容は完全ではありません。

なぜなら、移動や回転に何も制限をかけていないので、

壁や周囲のブロックを無視して移動・回転ができてしまうからです。

 

この状態ではテトリスが成り立ちませんね。

ではどうすれば制限をかけることができるのか?

次回はそのあたりを中心に解説していきたいと思います。

 

ではまた。

VBAテトリス 詳細編 テトリミノの落下スピードを変更する

どうもこんにちは。

 

引き続きマクロで作ったテトリスのコード解説をしていきます。

今回はテトリスの操作について触れようと思っていたのですが、

その前にテトリミノの落下スピードを変更する処理について触れたいと思います。

 

テトリスの処理の全体像で触れた通り、テトリミノの落下に関して、

よりテトリスっぽさを再現するため、LEVELに応じて落下スピードを変更する、

という要素を追加しています。

テトリミノの落下についてだけ触れており、こちらについて触れていませんでしたので、

次の処理の解説に入る前にこちらの解説をしたいと思います。

 

 

1.テトリスの挙動

テトリミノの落下スピードに関して、まずはテトリスの挙動を考えてみましょう。

 

テトリスには"LEVEL"という要素が存在しており、この"LEVEL"が上昇するのに応じて、

テトリミノの落下スピードが上昇していきます。

 

では、"LEVEL"は何に応じて上昇していくのかというと、

消去した行が10行に達するのに応じて1ずつ上昇します。

(なお、この仕様はテトリスミニの仕様です)

 

私が作ったテトリスでもこの仕様を採用しました。

 

以上を踏まえると、以下の処理ができればよい、ということになります。

・消去した行が10行に達するごとにLEVELを1増やす

・LEVELの値に応じて落下スピードを変える

 

一点目のLEVELを上昇させる処理については、

「SCORE等を計算する」処理の中で詳しく触れたいと思いますので、

今回は割愛します。

 

ということで、二点目の処理を行うためにどのようなコードを書けばよいかについて、

詳しく触れていきたいと思います。

 

2.LEVELの値に応じて落下スピードを変えるコード

前回触れた内容の中で、テトリミノの落下スピードを調整しているのは、

「Application.Wait」というコードである、と言いました。

(詳細は前回記事の「まとめ」の部分をご参照下さい)

programminghajimetemita.hatenablog.com

 

Application.Waitの待機時間がテトリミノの落下スピードに直結してますので、

これを"LEVEL"の値に応じて変化させてやればよい、ということです。

 

具体的にどのようなコードを書けばよいか、についてですが、

まず、"LEVEL"の値に応じて条件分岐させる必要がありますから、

"Select Case"により条件分岐を行います。

 

それぞれの分岐先でどのような処理をさせればよいか、についてですが、

"LEVEL"の上昇に応じて落下スピードも上昇していきますので、

LEVELが上昇するごとに待機時間を短くしていけばよいです。

 

つまり、

・LEVEL=1、ならば、待機時間1秒

・LEVEL=2、ならば、待機時間0.7秒

・LEVEL=3、ならば、待機時間0.4秒

といった具合です。

 

以上を踏まえて具体的なコードの内容です。

-本体-

・・・

Call clearblock(block, r, c)

Application.Wait[Now() + TimeValue("00:00:00.1")]

Call createblock(block, r + 1, c)

Call downspeed(level)

・・・

-落下スピードを変更するマクロー

Sub downspeed(level As Integer)

  Select Case level

    Case Is <= 1

      Application.Wait[Now() + TimeValue("00:00:01")]

    Case 2

      Application.Wait[Now() + TimeValue("00:00:00.7")]

    ・・・(中略)・・・

    Case Is >= 10

      Application.Wait[Now() + TimeValue("00:00:00.001")]

  End Select

End Sub

 

コードのポイントを解説します。

まず本体の方のコードです。

(ここで"本体"と言っているのは、テトリスを動かすメインの処理を行うマクロを指す、

と思ってください。)

 

前回の記事で触れたコードと違う部分が赤字にしている部分になります。

前回触れたコードでは常に待機時間が一定ということになりますが、

赤字の変更を加えることでLEVELの値に応じて落下スピードが変わるようになるわけです。

 

なお、上段の方のApplication.Waitには変更がありません。

このコードはテトリミノの落下の動きが滑らかに見えるようにするための調整として入れているものですので、

変更をしていません。

(これがないとテトリミノ落下時に残像が発生します、私のPCでは)

 

次に、落下スピードを変更するマクロについてです。

コードの内容は具体的なコード記述をする前に述べた内容の通りです。

(Select Caseで条件分岐、分岐先で待機時間を変更)

 

Select Caseの分岐の条件について、最初と最後のみ特定の値にしていません。

(最初はCase Is <= 1、最後はCase Is >= 10)

最初の方は"1以下"、最後の方は"10以上"という条件になります。

 

なぜそのようにしているかというと、

最初の方に関しては、"LEVEL"の初期値を設定していない(つまり最初は"LEVEL"=0)ためです。

初期値がない状態でもLEVEL=1と同じスピードにしたかったのでこのような条件になっています。

 

最後の方に関してはLEVEL10のスピードが最大スピードである、という仕様にするため、

このような条件にしました。

 

以上で落下スピードを変更する処理についての解説を終わります。

 

3.おわりに

今回は落下スピードを変更する処理について解説しました。

ここまで紹介した内容と比べると割と簡単な内容だったのではないでしょうか。

 

テトリスを作る、と考えるとどうやって作ればよいのかよくわからないと感じると思います。(私自身もそうでした)

ですが、テトリスの動きを分解して一つ一つのパーツを作る、と考えれば、

プログラミングに精通してなくても、意外とこうすればできるんじゃないかとアイデアが出てきて、

実際にパーツを完成させることができます。

 

この内容を見て、自分でもできそうだな、と感じてもらえればとてもうれしいです。

 

次回こそテトリスを操作する部分の処理に入りたいと思います。

VBAテトリス 詳細編 テトリミノが落下する

どうもこんにちは。

 

エクセルのマクロでテトリスを作ってみました。

 

programminghajimetemita.hatenablog.com

 

今回はテトリミノが自然に落下する動きをどうやって再現したか、

解説していきたいと思います。

 

 

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.おわりに

今回もかなり内容が長くなってしまいました。

説明が冗長になってわかりづらい部分もあるかもしれませんが、ご了承ください。

 

また、プログラミング初心者の私が書いたコードですので、

書き方がイケてない部分も多々あるかと思いますが、

そちらもご了承いただければと思います。

 

次回はテトリミノを操作する部分のコードについて解説します。

ではまた。

VBAテトリス 詳細編 テトリミノを画面に表示する2

 どうもこんにちは。

 

今回は前回に引き続き、テトリミノを画面に表示する方法について解説します。

前回の内容はこちらからどうぞ。

programminghajimetemita.hatenablog.com

 

今回の内容は”NEXT”の欄にテトリミノを表示する方法です。

以下の画像の画面真ん中上の部分です。

 

 

1.NEXTにテトリミノを表示する方法

NEXTの画面にテトリミノを表示する方法ですが、やることは以下の2つです。

① 1~7のランダムな整数値を発生させる

② ①の整数値に対応するテトリミノをNEXTの画面の位置に呼び出す

 

①は前回紹介した内容と同じようにRnd関数を使って行えばOKです。

(前回の内容は上部リンクからご確認ください)

 

②も1~7の値に対応するテトリミノの形のセル範囲を呼び出す部分は前回紹介した内容と同じです。

ランダム整数値の値ごとに条件分岐させる方法です(Select Caseを使う)。

 

前回紹介した通り、テトリミノの形のセル範囲は、位置を自由に変えられるように変数で表現しています。

なので、NEXTの位置に表示させるにはNEXTの位置の座標を代入してやればOKです。

セルの位置を表す座標は"( r, c )"と表現していますので、

rにNEXTの位置の行番号を、cにNEXTの位置の列番号を代入する、ということです。

 

以上でNEXTの画面にテトリミノを表示させることができます。

 

2.テトリミノをゲーム画面とNEXT画面に同時に表示する

ここからはこれまで紹介した内容を組み合わせて行う方法について触れていきます。

この方法によって、テトリミノをゲーム画面とNEXT画面に同時に表示することが可能になります。少しテトリスっぽくなってきましたね。

 

なお、ゲーム画面とは以下の画像の左の黒枠内の部分、

NEXT画面とは真ん中上部の黒枠部分を指しています。

 

では内容に入ります。

プログラムの内容に触れる前にテトリミノの表示に関してのテトリスの挙動について改めて触れたいと思います。

 

テトリスの挙動は以下のようなものになっているかと思います。

ゲーム開始直後(1巡目)

・テトリミノがランダムに選ばれてゲーム画面に表示される

・上記と同様、テトリミノがランダムに選ばれてNEXT画面に表示される

2巡目以降

・NEXT画面に表示されていたテトリミノがゲーム画面に表示される

・テトリミノがランダムに選ばれてNEXT画面に表示される

※テトリミノがゲーム画面に表示され、ゲーム画面の底orブロックに接地するまでの動きを1巡目、2巡目、、、と表現しています。

 

これを踏まえて、どういうプログラムを組めば上記挙動を実現できるか考えます。

 

実際に書いたコードは以下の通りです(一部割愛して記載してます)。

 

block = 0

Do

  If block = 0 Then

    block = Int(Rnd * 7) + 1

  Else

    block = next_block

  End If

 

  Select Case block

  ・・・

  End Select

 

  next_block = Int(Rnd * 7) + 1

 

  Select Case next_block

  ・・・

  End Select

  ・・・

Loop

 

では記載したコードを解説していきます。

・使用している変数

blockとnext_blockという変数を使っています。

blockの方はゲーム画面に表示するテトリミノを決定する変数、

next_blockの方はNEXT画面に表示するテトリミノを決定する変数です。

 

・1巡目と2巡目以降で処理内容を変える

テトリスの挙動で触れたとおり、ゲーム画面に表示するテトリミノの決定方法は1巡目と2巡目以降で異なります(1巡目はランダム、2巡目以降はNEXTに表示されていたもの)。

 

これは変数blockの値で条件分岐させることで実現しました。

繰り返し処理(Do~Loop)に入る前に"block=0"と処理しておくことで、

1巡目はblockが0、2巡目以降はblockが0以外の値になります。

 

その上で、blockが0であればランダム整数値になるように、

blockが0以外であればNEXT画面に表示されるもの(つまり、block=next_block)になるように分岐させれば、

1巡目と2巡目以降の処理を変えられるというわけです。

 

なお、2巡目以降、NEXT画面に表示されたものをゲーム画面に表示させるために、

"block=next_block"という処理を入れています。

辻褄があっているか確認するには具体的に考えるのがわかりやすいかと思いますので、

以下の単純化した具体例を見て確認してみてください。

 

①1巡目

繰り返し処理に入る前

  block = 0

繰り返し処理

・最初の条件分岐:block = 0 なので、blockは以下の値になる

  block = Int(Rnd * 7) + 1・・・結果、blockは1になったとする

・next_blockの値の決定:

  next_block = Int(Rnd * 7) + 1・・・結果、next_blockは4になったとする

 

以上より、ゲーム画面には"1"に対応するテトリミノが表示され、

NEXT画面には"4"に対応するテトリミノが表示される

 

②2巡目

繰り返し処理

・最初の条件分岐:block = 1であり、0ではないのでblockは以下の値になる

  block = next_block・・・結果、next_blockは4になる

・next_blockの値の決定:

  next_block = Int(Rnd * 7) + 1・・・結果、next_blockは6になったとする

 

以上より、ゲーム画面には"4"に対応するテトリミノが表示され、

(⇒前の周回でNEXT画面に表示されていたテトリミノが表示された)

NEXT画面には"6"に対応するテトリミノが表示される

 

・その他コードの補足

Int(Rnd * 7) + 1:ランダムな1~7の整数値を発生させるためのコードです。

Select Case block(next_block):

block(next_block)に対応するテトリミノを表示させるためのコードです。

変数をランダム整数値にすることでテトリミノがランダムに選ばれる挙動を再現しています。

 

いずれも前回の内容で詳しく解説していますので、詳細はそちらをご確認下さい。

 

3.おわりに

今回はNEXT画面にテトリミノを表示する方法、またそれと前回の内容を組み合わせて、

ゲーム画面とNEXT画面に同時に表示する方法について解説しました。

 

これだけではテトリミノが動かないのでまだゲームになりませんが、

少しはテトリスっぽくなったのではないかと思います。

 

次回以降、いよいよテトリミノを動かす処理について触れていきたいと思います。

 

ではまた。