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

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

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



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


スポンサーサイト

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






Chapter.115 [ ブロック崩しゲーム10:ボールの処理 ]

■ボールの処理に組み込む

前回と前々回、ちょっと数学的な話が続きました。少し難解な内容ではありましたが、大事なのはそれをプログラムにどう活用するかです。今回は、前回までで作成した線分が関係する処理を、それぞれボールの処理に組み込んでいきます。

今回作成するボールの処理は、かなり規模の大きなプロシージャになります。ポイントとしては、前回までに作成している線分関係の処理が、それぞれどのような意味のプロシージャなのか、それをよく理解して作成していくことです。ボール処理の様々なところで登場しますので、その都度どういう意味合いでプロシージャが呼び出されているのかを理解していないと、わけがわからなくなると思います。

前もって、再度前回までに作成しているプロシージャを確認しておきましょう。

CrossLine …… 線分の交差を判定しBool型で結果を返す
CrossPoint …… 線分の交点をVector型で返す

これが前回までに作成したふたつのプロシージャでしたね。交差を判定する CrossLine と、交点の座標を算出する CrossPoint のふたつを活用しながら、ブロック崩しの核となるボールの処理を作成していきます。CrossPoint のほうは、ユーザー定義型である Vector 型で結果を返してきますので、注意しましょう。


■ボール処理の概要

さて、それではボール処理を実際に見ていきます。

ボールを処理する際は、必要な情報をひとつひとつ取得しながら、様々なケースを想定して処理を組んでいきます。おおまかな手順で言うと、まずはボールの現在の座標(移動する前の位置)と、移動したあとの座標を明確にします。ここまでは、どんな場合でも必ず同じように行う必要がありますね。

移動後の座標が明確になると、今度はそこに壁やブロックが存在するのかどうかを判断します。もし、壁やブロックがそこにあって、ボールを反射させる必要があるなら、ボールの移動する向きを反転させたりしなければなりませんね。

このように考えると、ボールの処理のおおまかな流れは次のようになります。

ボールの現在位置と移動後の位置を明確にする
    ▼
そこに壁があるかを調べる
    ▼
壁があるならボールの移動方向を反転する
    ▼
ボールが画面上の上半分にあるか下半分にあるか調べる
    ▼
上半分にあるならブロックと衝突しているかもしれない
    ▼
ブロックとの衝突があるのかどうか調べる
    ▼
ブロックと衝突しているならボールの移動方向を反転する
    ▼
下半分にあるならバーと衝突しているかもしれない
    ▼
バーとの衝突があるのかどうか調べる
    ▼
バーと衝突しているならボールの移動方向を反転する
    ▼
最終的にボールがどこに置かれるのか決定する

こんな感じになりますね。


■ボールの処理

このプロシージャは大別してみっつのブロックから成り立っています。それぞれのブロックの役割をしっかり理解していくことが大切です。

各ブロックはコードを色分けして表示しています。それを参考にしながら考えてみてください。

Sub Ball_Move()

    Dim L As Long
    Dim S As Single
    Dim SS As Single

    Dim fX As Boolean
    '横方向での衝突があったかどうかを保持するフラグ

    Dim fY As Boolean
    '縦方向での衝突があったかどうかを保持するフラグ

    Dim oX As Single
    '線分の処理に利用するX座標

    Dim oY As Single
    '線分の処理に利用するY座標

    Dim CP As Vector
    '線分の交点座標を保持するためのVector型変数
    
    With Ball
        fX = False 'フラグを初期化する
        fY = False 'フラグを初期化する
        .aX = .bX + .vX * .Sp '移動後のボールのX座標を求める
        .aY = .bY + .vY * .Sp '移動後のボールのY座標を求める
        CP.X = .aX
        CP.Y = .aY
        If .aX < W_Ball / 2 Then
            fX = True
            CP = CrossPoint(.aX, .aY, _
                            .bX, .bY, _
                            W_Ball / 2, 0, _
                            W_Ball / 2, H_Field)
        End If
        If .aX > W_Field - W_Ball / 2 Then
            fX = True
            CP = CrossPoint(.aX, .aY, _
                            .bX, .bY, _
                            W_Field - W_Ball / 2, 0, _
                            W_Field - W_Ball / 2, H_Field)
        End If
        If .aY < W_Ball / 2 Then
            fY = True
            CP = CrossPoint(.aX, .aY, _
                            .bX, .bY, _
                            0, W_Ball / 2, _
                            W_Field, W_Ball / 2)
        End If
        If .aY > H_Field Then
            .Vis = False
        End If

        If .aY <= H_Field / 2 Then
            For L = 1 To 25
                If Block(L).Vis Then
                    If Not fX Then
                        SS = W_Ball / 2
                        fX = False
                        If .vX < 0 Then
                            S = Block(L).X + W_Block / 2
                            oX = S + SS
                        Else
                            S = Block(L).X - W_Block / 2
                            oX = S - SS
                        End If
                        fX = CrossLine(.aX, .aY, _
                                       .bX, .bY, _
                                       oX, Block(L).Y - H_Block / 2 - SS, _
                                       oX, Block(L).Y + H_Block / 2 + SS)
                        If fX Then
                            CP = CrossPoint(.aX, .aY, _
                                            .bX, .bY, _
                                            oX, Block(L).Y - H_Block / 2 - SS, _
                                            oX, Block(L).Y + H_Block / 2 + SS)
                            Block(L).Vis = False
                            If .Sp < 12.5 Then .Sp = .Sp + 0.5
                        End If
                    End If
                    If Not fY Then
                        SS = W_Ball / 2
                        fY = False
                        If .vY < 0 Then
                            S = Block(L).Y + H_Block / 2
                            oY = S + SS
                        Else
                            S = Block(L).Y - H_Block / 2
                            oY = S - SS
                        End If
                        fY = CrossLine(.aX, .aY, _
                                       .bX, .bY, _
                                       Block(L).X - W_Block / 2 - SS, oY, _
                                       Block(L).X + W_Block / 2 + SS, oY)
                        If fY Then
                            CP = CrossPoint(.aX, .aY, _
                                            .bX, .bY, _
                                            Block(L).X - W_Block / 2 - SS, oY, _
                                            Block(L).X + W_Block / 2 + SS, oY)
                            Block(L).Vis = False
                            If .Sp < 12.5 Then .Sp = .Sp + 0.5
                        End If
                    End If
                End If
                UserForm1.Controls("Label" & L).Visible = Block(L).Vis
            Next
            If fX Then
                .vX = -.vX
                .aX = CP.X
                .aY = CP.Y
            End If
            If fY Then
                .vY = -.vY
                .aX = CP.X
                .aY = CP.Y
            End If

        Else
            If .vY > 0 Then
                If Not fX And Not fY Then
                    SS = W_Ball / 2
                    fX = False
                    If .vX < 0 Then
                        S = Bar.X + W_Bar / 2
                        oX = S + SS
                    Else
                        S = Bar.X - W_Bar / 2
                        oX = S - SS
                    End If
                    fX = CrossLine(.aX, .aY, _
                                   .bX, .bY, _
                                   oX, Bar.Y - H_Bar / 2 - SS, _
                                   oX, Bar.Y + H_Bar / 2 + SS)
                    If fX Then
                        CP = CrossPoint(.aX, .aY, _
                                        .bX, .bY, _
                                        oX, Bar.Y - H_Bar / 2 - SS, _
                                        oX, Bar.Y + H_Bar / 2 + SS)
                    End If
                    fY = False
                    S = Bar.Y - H_Bar / 2
                    oY = S - SS
                    fY = CrossLine(.aX, .aY, _
                                   .bX, .bY, _
                                   Bar.X - W_Bar / 2 - SS, oY, _
                                   Bar.X + W_Bar / 2 + SS, oY)
                    If fY Then
                        CP = CrossPoint(.aX, .aY, _
                                        .bX, .bY, _
                                        Bar.X - W_Bar / 2 - SS, oY, _
                                        Bar.X + W_Bar / 2 + SS, oY)
                    End If
                End If
            End If
            If fX Then
                .vX = -.vX
                .aX = CP.X
                .aY = CP.Y
            End If
            If fY Then
                .vY = -.vY
                .aX = CP.X
                .aY = CP.Y
                S = Bar.X - W_Bar / 2
                SS = Bar.X + W_Bar / 2
                If .aX >= S Then
                    If .aX <= S + (W_Bar / 3) Then .vX = -Abs(.vX)
                End If
                If .aX <= SS Then
                    If .aX >= SS - (W_Bar / 3) Then .vX = Abs(.vX)
                End If
                If .aX > S + (W_Bar / 3) Then
                    If .aX < SS - (W_Bar / 3) Then .vY = -Abs(.vY) * 1.05
                End If
            End If

        End If
        If .aX < W_Ball / 2 Then .aX = W_Ball / 2
        If .aX > W_Field - W_Ball / 2 Then .aX = W_Field - W_Ball / 2
        If .aY < W_Ball / 2 Then .aY = W_Ball / 2
        .bX = .aX
        .bY = .aY
    End With
    
    With UserForm1
        .Ima_ball.Left = Ball.aX - W_Ball / 2
        .Ima_ball.Top = Ball.aY - W_Ball / 2
    End With
    
End Sub

とても長いプロシージャですね。
ちょっと気がめいりそうになりますが、順番に見ていきましょう。

まず、最初の黄色い文字の部分では、壁との衝突があったのかどうかを調べています。あらかじめ宣言してあった定数をうまくつかって、ボールと、左・上・右の壁との衝突があったのかどうかを判定しています。

壁の向こう側にめりこんでしまうような座標にボールが到達している場合には、これは衝突をしていることと同じです。ボールの移動前の座標と、移動後の座標をつないで一本の線分を作り、さらに、壁を一本の線分と考えて、線分同士の交差判定を行っていますね。(プロシージャ CrossPoint を呼び出している部分)

変数 CP は、Vector 型の変数です。ですから、プロシージャ CrossPoint が返してくる交点座標の情報を受け取ることができます。CrossPoint を呼び出したあと、変数 CP に取得されている座標情報を調べれば、ボールが存在するべき座標がわかるというわけです。

もし、下の壁よりもボールが向こう側にある場合は、これはボールを落としてしまっているということになりますね。ですからこの場合は、ボールを見えないようにしています。
これで、ボールと壁との処理はすべて完了です。


続いて、ボールが画面上の上半分にいるのか、それとも下半分にいるのかを調べ、その結果によって処理を分割しています。

ボールが上半分にいる場合には、ブロックと衝突をしている可能性があるため、ブロックとの衝突があるのかどうかを調べていきます。これが緑色の文字で書かれているコードの部分ですね。

ブロックひとつひとつと順番に衝突判定を行っていき、もし衝突が発生しているなら壁のときと同様に線分による処理を行います。最終的に線分同士の交点座標を算出して、それをボールの最終的な移動後の位置として設定するわけです。

ボールが下半分にいる場合は、ブロックと衝突する可能性はありません。その代わり、バーとの衝突が発生している可能性はありますね。バーとの衝突判定を行っているのは水色の文字で示されている部分です。
ここでも、ブロックとボールのときに行ったのと同様に、バーとボールの衝突判定を行っています。もしバーとボールが衝突しているのなら、線分による衝突判定を行ってボールの最終的な位置を算出しています。


どのような衝突判定の場合でも、まずはプロシージャ CrossLine を使って、線分の交差があるのかどうかを判定しています。ボールの移動前の座標と、移動後の座標を繋いで一本の線分を作り、対象となるブロックやバーの一辺を線分として考え、これら二本の線分の交差を判定します。
線分同士が交差しているなら、これは衝突が起こっていることと同じになります。衝突が起こっているなら、今度は線分と線分との交点を算出するために CrossPoint を呼び出し、交点座標にボールを配置することで、ボールの最終的な座標位置を決定しています。


■まとめ

さて、はたしてこの解説を見ている人が、この説明だけでどれだけ理解できているのか不安ですが、いかがでしょうか。プロシージャ自体がかなり大きいので、ちょっと理解するのが大変かもしれません。

ポイントとなるのは、コードにつけられた色を参考にして、その部分が一体どのような処理を行っている部分なのかをしっかり理解して考えることです。
今現在は、ボールと何との衝突を判定している部分なのか、それが明確になっていないとなかなかスムーズに理解することは難しいでしょう。壁、ブロック、バー、そしてボール。これらがどのような関係にあるのかをしっかり理解して処理を考えましょう。

実際に動くサンプルは、次回、ダウンロードできるようにするつもりです。
そして、次回は恐らくブロック崩しゲームの最終回になると思います。完成したブロック崩しゲームのサンプルは、そのときにお披露目します。



■格言

現在の状態を把握して処理する
線分関連の処理の意味を理解する


前回までの上積みが、ここへきて重要になります。
意味がわからない場合は少し戻って、もう一度考えてみるのもいいかもしれません。

スポンサーサイト






Chapter.116 [ ブロック崩しゲーム11:最終調整して完成へ ]

■こまかな調整

前回のボールの処理を実装できたら、あとはこまかい部分の調整をすればブロック崩しゲームがいよいよ完成します。

現在の状態では、まだボールを落としてしまったあとの処理や、ゲームオーバーの処理、ゲームクリア時の処理などが未実装となっています。ボールの処理だけを実装させても、それではまだブロック崩し全体の構造としては未完成なのですね。
今回は、これらの処理を行う小規模なプロシージャを作成し、ブロック崩しを完成させましょう。


■ブロックはあるのか

ブロック崩しのゲームクリア条件といえば、画面上のブロックが全て破壊されたかどうかというのがセオリーです。今回のブロック崩しも、これをクリアの条件として考えてみましょう。

画面上のブロックが全て消えているかどうか調べるには、ブロックひとつひとつを参照しながら、それぞれが表示されているかどうかを順番に調べていけばいいですね。これは繰り返し処理を行う For 文を使えば簡単そうですね。

Function Block_Check() As Boolean

    Dim L As Long
    Dim F As Boolean
    
    F = False '①
    
    For L = 1 To 25
        F = F Or Block(L).Vis '②
    Next
    
    Block_Check = F '③
    
End Function

この Block_Check というプロシージャは、ブロックが全て破壊されたかどうかを調べるためのプロシージャです。全部で25個あるブロックをひとつひとつ参照していき、それぞれが表示されているかどうかを調べています。

繰り返し処理の中では、「 F = F Or Block(L).Vis 」という具合に何かを行っていますね。変数 F は、Boolean 型の変数です。①で示すように、繰り返し処理の前にまず False を設定しておきます。そのあと、②の部分の繰り返し処理中に、Or 演算子をうまく使いながら各ブロックの表示状態を調べていきます。
Or 演算子を使って Boolean 型のデータを扱う場合には、比較対象となるどちらか一方だけでも True になっていれば、結果は True になります。最終的に③の部分で変数 F True だったとすると、少なくともひとつは表示されているブロックがあったことになります。

つまり、この Block_Check というプロシージャを呼び出したとき、結果が True だった場合には表示されているブロックが存在することになりますし、逆に結果が False だった場合には、ひとつも表示されているブロックが無かったということになります。

この仕組みを使えば、ブロックが現在画面上に存在しているのかどうかを調べることができますので、ゲームをクリアしているかどうか、それを明確に調べることができるのですね。


■メインループを完成させる

以前にメインループの雛形を載せたことがありましたね。その際には、まだまだパーツとなる各処理のプロシージャが完成していませんでした。
現状は全てのパーツが出揃っていますので、メインループを完成させましょう。

Sub main()

    Dim F As Boolean
    Dim Time_Count As Long
    
    Do Until F
    
        Time_Count = GetTickCount
        
        If Ball.Vis Then 'ボールが見えている場合の処理
            Bar_Move
            Ball_Move
            If Not Block_Check Then
                MsgBox "game_clear!!"
                F = True
            End If
        Else             'ボールが見えてない場合の処理
            MsgBox "out!"
            Spare = Spare - 1 'スペアをひとつ減らす
            If Spare >= 0 Then 'スペアの残数を調べる
                Init
                DoEvents
                Sleep 1000
            Else
                MsgBox "game over"
                F = True
            End If
        End If
        
        DoEvents
        
        Do
            Sleep 1
        Loop Until GetTickCount - Time_Count > 20
        
        If GetAsyncKeyState(90) Then F = True
        
    Loop
    
End Sub

ここで登場している変数 Spare は、初めて登場する変数です。
ただ、この変数はモジュールレベルで宣言しておく変数で、キーワード Public を使ってモジュールの宣言セクションで宣言する必要があります。

理由は簡単ですね。この変数 Spare は、ボールのスペアを管理する変数です。どのようなプロシージャからでも参照できるようにしておく必要がありますし、ゲームの開始から終了まで、常に値を保持しておく必要があります。
このような理由から、変数 Spare は宣言セクションで、グローバルな変数として宣言します。最後にコードの全文を掲載しますので、注意してみてくださいね。

また、スペアの数を画面上に表示するという部分を追加しますので、この時点でユーザーフォーム上にラベルコントロールを追加しています。ラベルコントロールの名前は Lab_spare とし、スタートボタンの役割を果たすコマンドボタンの隣に配置しています。

670.gif


■コード全文

以下に、今回のブロック崩しのコードを全文掲載します。
全体で見ると、そこそこ長いプログラムになっていますが、ボールの処理以外はそれほど大きなプロシージャはありません。

各処理ごとにこまかくプロシージャを作成しておくことで、わかりやすく、メンテナンスしやすい構造になります。超巨大なプロシージャを気合で作成するよりも、このようなかたちで目的別にプロシージャを細かく作成するほうが、様々な面で優れています。

プログラムの作成に不慣れなうちは、すべての処理をひとつのプロシージャ内でまとめてしまいがちです。しかし、これをやってしまうと何か不具合が発生した際に、その不具合がどこで発生しているものなのか特定しにくくなるだけでなく、修正を行ったら別の部分で不具合が発生してしまったりと、プロジェクト全体が連鎖的に駄目になることがあります。その点、細切れにプロシージャを作成しておくようにすれば、不具合はその都度、プロシージャ内のみで完結していることが多いので、メンテナンスも修正も非常に楽になります。

一見すると超巨大なプロジェクトに見えるかもしれませんが、実際は細かいパーツの組み合わせによって、最終的にひとつのゲームが完成しているかたちになっています。それぞれのプロシージャの役割や使い方をよく理解し、メンテナンス性の高いプロジェクトを作成するためのヒントにしてみてください。

'標準モジュールに記載
'API宣言
Declare Function GetTickCount Lib "kernel32" () As Long
Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Long) As Long
Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMillsecounds As Long)


'構造体定義
Type Balls
    aX As Single
    aY As Single
    bX As Single
    bY As Single
    vX As Single
    vY As Single
    Sp As Single
    Vis As Boolean
End Type

Type Blocks
    X As Single
    Y As Single
    Vis As Boolean
End Type

Type Vector
    X As Single
    Y As Single
End Type


'変数宣言
Public Ball As Balls
Public Block(1 To 25) As Blocks
Public Bar As Vector
Public Spare As Long


'定数宣言
Const W_Field As Single = 250
Const H_Field As Single = 300
Const W_Ball As Single = 10
Const W_Block As Single = 50
Const H_Block As Single = 20
Const T_Block As Single = 20
Const W_Bar As Single = 48
Const H_Bar As Single = 12
Const T_Bar As Single = 280

Const PI As Single = 3.14159


'メインループ
Sub main()

    Dim F As Boolean
    Dim Time_Count As Long
    
    Do Until F
    
        Time_Count = GetTickCount
        
        If Ball.Vis Then
            Bar_Move
            Ball_Move
            If Not Block_Check Then
                MsgBox "game_clear!!"
                F = True
            End If
        Else
            MsgBox "out!"
            Spare = Spare - 1
            If Spare >= 0 Then
                Init
                DoEvents
                Sleep 1000
            Else
                MsgBox "game over"
                F = True
            End If
        End If
        
        DoEvents
        
        Do
            Sleep 1
        Loop Until GetTickCount - Time_Count > 20
        
        If GetAsyncKeyState(90) Then F = True
        
    Loop
    
End Sub


'初期化処理
Sub Init()

    Dim L As Long
    Dim S As Single
    
    Randomize
    
    With Ball
        L = Int(Rnd * 30) + 30
        S = L * PI / 180
        .aX = 0
        .aY = 0
        .bX = W_Field / 2
        .bY = T_Bar - W_Ball / 2
        .vX = Cos(S)
        .vY = Sin(S)
        .Sp = 3
        .Vis = True
        If Int(Rnd * 2) = 0 Then .vX = -.vX
    End With
    
    With Bar
        .X = W_Field / 2
        .Y = T_Bar + H_Bar / 2
    End With
    
    With UserForm1
        .Ima_bar.Left = (W_Field - W_Bar) / 2
        .Ima_bar.Top = T_Bar
        .Ima_ball.Left = (W_Field - W_Ball) / 2
        .Ima_ball.Top = T_Bar - W_Ball
        .Lab_spare.Caption = "Spare : " & Format(Spare, "00")
    End With
        
End Sub


'ブロックの初期化
Sub Init_Block(Stage As Long)

    Dim L As Long
    Dim LL As Long
    Dim Cell_Row As Long
    Dim Cell_Col As Long
    
    For L = 0 To 24
        With ThisWorkbook.Worksheets("map")
            Cell_Row = (Stage * 5 - 4) + (L \ 5)
            Cell_Col = (L Mod 5) + 1
            If .Cells(Cell_Row, Cell_Col).Value > 0 Then
                With Block(L + 1)
                    LL = L Mod 5
                    .X = (W_Block / 2) + (LL * W_Block)
                    LL = L \ 5
                    .Y = T_Block + (H_Block / 2) + (LL * H_Block)
                    .Vis = True
                    UserForm1.Controls("Label" & L + 1).Visible = True
                End With
            Else
                With Block(L + 1)
                    .Vis = False
                    UserForm1.Controls("Label" & L + 1).Visible = False
                End With
            End If
        End With
    Next

End Sub


'ボールの処理
Sub Ball_Move()

    Dim L As Long
    Dim S As Single
    Dim SS As Single
    Dim fX As Boolean
    Dim fY As Boolean
    Dim oX As Single
    Dim oY As Single
    Dim CP As Vector
    
    With Ball
        fX = False
        fY = False
        .aX = .bX + .vX * .Sp
        .aY = .bY + .vY * .Sp
        CP.X = .aX
        CP.Y = .aY
        If .aX < W_Ball / 2 Then
            fX = True
            CP = CrossPoint(.aX, .aY, _
                            .bX, .bY, _
                            W_Ball / 2, 0, _
                            W_Ball / 2, H_Field)
        End If
        If .aX > W_Field - W_Ball / 2 Then
            fX = True
            CP = CrossPoint(.aX, .aY, _
                            .bX, .bY, _
                            W_Field - W_Ball / 2, 0, _
                            W_Field - W_Ball / 2, H_Field)
        End If
        If .aY < W_Ball / 2 Then
            fY = True
            CP = CrossPoint(.aX, .aY, _
                            .bX, .bY, _
                            0, W_Ball / 2, _
                            W_Field, W_Ball / 2)
        End If
        If .aY > H_Field Then
            .Vis = False
        End If
        If .aY <= H_Field / 2 Then
            For L = 1 To 25
                If Block(L).Vis Then
                    If Not fX Then
                        SS = W_Ball / 2
                        fX = False
                        If .vX < 0 Then
                            S = Block(L).X + W_Block / 2
                            oX = S + SS
                        Else
                            S = Block(L).X - W_Block / 2
                            oX = S - SS
                        End If
                        fX = CrossLine(.aX, .aY, _
                                       .bX, .bY, _
                                       oX, Block(L).Y - H_Block / 2 - SS, _
                                       oX, Block(L).Y + H_Block / 2 + SS)
                        If fX Then
                            CP = CrossPoint(.aX, .aY, _
                                            .bX, .bY, _
                                            oX, Block(L).Y - H_Block / 2 - SS, _
                                            oX, Block(L).Y + H_Block / 2 + SS)
                            Block(L).Vis = False
                            If .Sp < 12.5 Then .Sp = .Sp + 0.5
                        End If
                    End If
                    If Not fY Then
                        SS = W_Ball / 2
                        fY = False
                        If .vY < 0 Then
                            S = Block(L).Y + H_Block / 2
                            oY = S + SS
                        Else
                            S = Block(L).Y - H_Block / 2
                            oY = S - SS
                        End If
                        fY = CrossLine(.aX, .aY, _
                                       .bX, .bY, _
                                       Block(L).X - W_Block / 2 - SS, oY, _
                                       Block(L).X + W_Block / 2 + SS, oY)
                        If fY Then
                            CP = CrossPoint(.aX, .aY, _
                                            .bX, .bY, _
                                            Block(L).X - W_Block / 2 - SS, oY, _
                                            Block(L).X + W_Block / 2 + SS, oY)
                            Block(L).Vis = False
                            If .Sp < 12.5 Then .Sp = .Sp + 0.5
                        End If
                    End If
                End If
                UserForm1.Controls("Label" & L).Visible = Block(L).Vis
            Next
            If fX Then
                .vX = -.vX
                .aX = CP.X
                .aY = CP.Y
            End If
            If fY Then
                .vY = -.vY
                .aX = CP.X
                .aY = CP.Y
            End If
        Else
            If .vY > 0 Then
                If Not fX And Not fY Then
                    SS = W_Ball / 2
                    fX = False
                    If .vX < 0 Then
                        S = Bar.X + W_Bar / 2
                        oX = S + SS
                    Else
                        S = Bar.X - W_Bar / 2
                        oX = S - SS
                    End If
                    fX = CrossLine(.aX, .aY, _
                                   .bX, .bY, _
                                   oX, Bar.Y - H_Bar / 2 - SS, _
                                   oX, Bar.Y + H_Bar / 2 + SS)
                    If fX Then
                        CP = CrossPoint(.aX, .aY, _
                                        .bX, .bY, _
                                        oX, Bar.Y - H_Bar / 2 - SS, _
                                        oX, Bar.Y + H_Bar / 2 + SS)
                    End If
                    fY = False
                    S = Bar.Y - H_Bar / 2
                    oY = S - SS
                    fY = CrossLine(.aX, .aY, _
                                   .bX, .bY, _
                                   Bar.X - W_Bar / 2 - SS, oY, _
                                   Bar.X + W_Bar / 2 + SS, oY)
                    If fY Then
                        CP = CrossPoint(.aX, .aY, _
                                        .bX, .bY, _
                                        Bar.X - W_Bar / 2 - SS, oY, _
                                        Bar.X + W_Bar / 2 + SS, oY)
                    End If
                End If
            End If
            If fX Then
                .vX = -.vX
                .aX = CP.X
                .aY = CP.Y
            End If
            If fY Then
                .vY = -.vY
                .aX = CP.X
                .aY = CP.Y
                S = Bar.X - W_Bar / 2
                SS = Bar.X + W_Bar / 2
                If .aX >= S Then
                    If .aX <= S + (W_Bar / 3) Then .vX = -Abs(.vX)
                End If
                If .aX <= SS Then
                    If .aX >= SS - (W_Bar / 3) Then .vX = Abs(.vX)
                End If
                If .aX > S + (W_Bar / 3) Then
                    If .aX < SS - (W_Bar / 3) Then .vY = -Abs(.vY) * 1.05
                End If
            End If
        End If
        If .aX < W_Ball / 2 Then .aX = W_Ball / 2
        If .aX > W_Field - W_Ball / 2 Then .aX = W_Field - W_Ball / 2
        If .aY < W_Ball / 2 Then .aY = W_Ball / 2
        .bX = .aX
        .bY = .aY
    End With
    
    With UserForm1
        .Ima_ball.Left = Ball.aX - W_Ball / 2
        .Ima_ball.Top = Ball.aY - W_Ball / 2
    End With
    
End Sub


'バーの処理
Sub Bar_Move()

    Dim S As Single
    
    With Bar
        If GetAsyncKeyState(37) Then .X = .X - 7.5
        S = W_Bar / 2
        If .X < S Then .X = S
        If GetAsyncKeyState(39) Then .X = .X + 7.5
        S = W_Field - W_Bar / 2
        If .X > S Then .X = S
    End With
    
    With UserForm1
        .Ima_bar.Left = Bar.X - W_Bar / 2
    End With
    
End Sub


'ブロックの可視チェック
Function Block_Check() As Boolean

    Dim L As Long
    Dim F As Boolean
    
    F = False
    
    For L = 1 To 25
        F = F Or Block(L).Vis
    Next
    
    Block_Check = F
    
End Function


'線分交差判定関数
Function CrossLine(x1 As Single, y1 As Single, _
                   x2 As Single, y2 As Single, _
                   x3 As Single, y3 As Single, _
                   x4 As Single, y4 As Single) As Boolean

    Dim Deno As Single
    Dim Nume1 As Single
    Dim Nume2 As Single
    Dim F1 As Boolean
    Dim F2 As Boolean
    Dim F3 As Boolean
    
    Deno = (x3 - x1) * (y4 - y1) - (y3 - y1) * (x4 - x1)
    
    If Deno = 0 Then
        F1 = (x3 - x1) * (x4 - x1) + (y3 - y1) * (y4 - y1) <= 0
        F2 = (x3 - x2) * (x4 - x2) + (y3 - y2) * (y4 - y2) <= 0
        F3 = (x1 - x3) * (x2 - x3) + (y1 - y3) * (y2 - y3) <= 0
        CrossLine = F1 Or F2 Or F3
    Else
        Nume1 = (y4 - y1) * (x2 - x1) - (x4 - x1) * (y2 - y1)
        Nume2 = (x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)
        F1 = ((Nume1 + Nume2) / Deno >= 1)
        If Deno < 0 Then
            F2 = (Nume1 <= 0)
            F3 = (Nume2 <= 0)
        Else
            F2 = (Nume1 >= 0)
            F3 = (Nume2 >= 0)
        End If
        CrossLine = F1 And F2 And F3
    End If
    
End Function


'線分の交点座標計算関数
Function CrossPoint(x1 As Single, y1 As Single, _
                    x2 As Single, y2 As Single, _
                    x3 As Single, y3 As Single, _
                    x4 As Single, y4 As Single) As Vector
    
    Dim a1 As Single
    Dim a2 As Single
    Dim b1 As Single
    Dim b2 As Single
    Dim c1 As Single
    Dim c2 As Single
    
    a1 = y1 - y2
    a2 = y3 - y4
    b1 = x2 - x1
    b2 = x4 - x3
    c1 = x1 * y2 - x2 * y1
    c2 = x3 * y4 - x4 * y3
    
    CrossPoint.X = (b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1)
    CrossPoint.Y = (a1 * c2 - a2 * c1) / (a2 * b1 - a1 * b2)
                   
End Function


'以下はフォームモジュールに記載
'スタートボタンクリックイベント
Private Sub Com_start_Click()

    Com_start.Enabled = False
    
    Spare = 3
    
    Init
    
    Init_Block 1
    
    main
    
    Com_start.Enabled = True
    
End Sub


'ユーザーフォームイニシャライズイベント
Private Sub UserForm_Initialize()

    Init
    
    Init_Block 1
    
End Sub


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

    End
    
End Sub

さて、これにて長かったブロック崩しゲームの作成講座も終了です。
実際に動くサンプルを別館にてアップしてありますので、そちらも参考にしながらがんばってみてください。

今回のブロック崩しの大きなポイントとなるのは、ベクトルを用いた移動処理、線分を用いた交差判定と交点座標計算です。数学的な知識が必要となる部分ではありますが、要は使い方さえわかっていれば、様々なことに流用できますので便利です。

計算の中身がどのような理論になっているのかはともかく、プログラミングという意味ではまず動かしてみる、体感してみるということが大事です。難しい理論ばかりが先行してしまい、いつまでたってもプログラムが動かないというのは大変なストレスになります。意味はわからなくても、とりあえず思ったとおりに動かすことができた、という感覚が一番大事です。

わからないことが恥ずかしいのではなく、挑戦しないのがもったいないのです。たんなるコピープログラムであっても、それが自分なりに創意工夫した結果であれば、私はそれでいいのだと思っています。サンプルをいじりながら、自分なりに想像を膨らましてみてくださいね。

ちなみに、今回のブロック崩しゲームサンプルでは、バーのどこに当たったのかによってボールの跳ね返る方向が変わるようになっています。また、ボールが壁以外の場所に当たった際には少しだけボールの移動スピードがアップするようになっています。本来であれば、ボールが壁やブロックに当たった際に、ランダムでほんの少しだけ方向を変化させるほうがいいのですが、今回は簡易版のサンプルということで、そこまでは実装していません。まぁ、普通に遊べる形にはしっかりできていますので、ご安心ください。


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



■格言

難しいプログラムが偉いのではない
動かしてみて感動することが大切


やっとブロック崩しが完成しましたね。お疲れ様でした。







上級講座1:一歩上いくテクニック

初心者でも取り組みやすいVBA。しかしその世界は実に深い。極めれば市販ゲーム顔負けの超高度な処理を実現することもできます。

当講座の記事数も110回を超え、徐々に『 脱初心者 』というレベルに達しつつあります。本来なら、もっといろいろなケースに対応できる細かなテクニックを解説するべきなのかもしれませんが、そろそろ上級者コースを始めてもいい頃合かなと思います。

上級講座1では、一歩上いくテクニックと題し、テキストファイルの扱い方やクラスモジュールについて取り上げます。知らなければ知らないで、別段困るというものでもありません。しかし、知っていることで様々な応用ができるようになり、全く別次元の処理が実現できる場面も出てくるでしょう。

難易度も若干上がり、すぐには理解できないことも、もしかしたらあるかもしれません。でも大事なことは諦めないことです。そしてまずはやってみること。いじっているうちに、不思議となじんでくるものです。サンプルなどを参考にしながら、是非、初心者の領域を脱して新たなVBAワールドに飛び込んでみてください。きっと、今までは想像もつかなかった景色が広がっていますよ。

それでは、がんばっていってみましょう。



    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.117 [ テキストファイル操作基礎 ]

■テキストファイルでなにするの

テキストファイル、と唐突に言われて、皆さんはどんなものを想像するでしょうか。きっと、なんとなくつまらないもののイメージが浮かぶ人が多いんじゃないでしょうか。

テキストファイルは、プログラミングという観点からすると非常に面白い素材です。噛めば噛むほど味が出るというか、いったんテキストファイルの扱いに慣れてしまうと、その便利さの虜になること請け合いです。設定やリプレイデータの保存はもちろんのこと、独自ファイルの書き出しや読み込みなど、ゲームの開発に役立つ様々な処理が実現できます。

今回は、テキストファイル操作の基本をやってみます。もう知っているよ、という方もいるかもしれませんが、基本的なところからしっかり見ていくことにしましょう。


■ファイルを開くってどういうこと

そもそも、ファイルを開くというのはどういうことを指すのでしょうか。
今回扱うテキストファイルの代表格といえば、拡張子 txt のテキストファイルですよね。

680.gif

このファイルを開くとすると、皆さんはどうしますか?
ダブルクリックして、ファイルを開きますか?

普通にダブルクリックすると、システムで割り当てられた既定のテキストエディタが起動すると思います。Windows なら、デフォルトでメモ帳が起動するはずですね。
これは確かにファイルを開いていますが、今回解説するテキストファイルの操作とは若干意味合いが違っています。

ダブルクリックしてテキストファイルを開く、という行為は、正確には『 テキストエディタソフトでファイルを開く 』ですね。VBA でファイルを開くことと、テキストエディタなどのソフトでファイルを開くこととは、根本的に意味が違います。

私達がやりたいことは、こうやってテキストエディタを介してファイルを開くことではありません。直接、ファイルを操作することですよね。この点を間違えないようにしましょう。工夫次第では、テキストエディタを介してファイルを操作することもできますが、これは逆に難しいです。それに、面倒ですしね。


■書き出してみる

それでは実際にテキストファイルを操作してみます。
VBA からテキストファイルを操作する方法はいろいろありますが、今回は最も簡単な方法でやってみます。

テキストファイルを新しく作成するための、専用のステートメントというのは実は存在しません。ファイルをオープンする、つまりファイルを開くというステートメントがその代用として機能します。

Sub test()

    Dim L As Long
    Dim fName As String
    Dim fString As String
    
    L = FreeFile '①
    fName = ThisWorkbook.Path & "\test.txt" '②
    fString = "出力テスト" '③
    
    Open fName For Output As #L '④
    Print #L, fString '⑤
    Close #L '⑥

End Sub

このコードを実行すると、実行されたファイルと同じディレクトリ(フォルダ)に、新しく test.txt というテキストファイルが生成されます。

①では、変数 L に何かを取得していますね。
Windows がファイルを開こうとするとき、全てのファイルに対して識別番号が割り振られます。プログラムがファイルにアクセスしようとする際には、このファイル番号が看板の役割を果たし、それぞれのファイルを識別するようになっているのです。もしも同じ識別番号を持つファイルが同時に存在してしまったら、いろいろと不具合が出る可能性が出てきます。①の部分ではこの問題を解消するために、現在使われていないファイル番号を自動的に取得するようにしています。

FreeFile 関数は、現在未使用となっているファイル番号を返してくれる関数です。この関数を使えば、他のファイルと同じ識別番号を使うことがなくなるので安心です。①では、変数 L に未使用のファイル番号を取得していたのですね。

さて、次は②です。
ちょっと VBA の理解が深い人なら、ここで何をしているかは一目瞭然ですね。変数 fName には、コードが実行されている Excel ブックのパスと、test.txt というファイル名が入るようになっています。最終的に、このとき変数 fName に入ったパスに、テキストファイルが生成されます。

③は、生成されたテキストファイルに書き出される文字列を指定しています。String 型の変数に書き出したい文字列を入れておきます。


さて、今回の肝となるのが④の部分ですね。
ここで登場している Open ステートメントこそが、ファイルを操作しているステートメントになります。その名の通り、ファイルを開くことができるステートメントですが、開こうとしているファイルが存在しない場合は、新しくファイルを生成します。厳密には、必ずしも新しくファイルが生成されるわけではないのですが、それはこのステートメントにどのような引数を与えたかによって変わってきます。

Open ステートメント

Open ファイルのパス For モード [Access アクセスモード] [ロックモード] As #ファイル番号

モード
Append開いたファイルに追記する
Binaryバイナリデータとしてファイルを開く
Inputファイルを読み込む
Outputファイルに書き出す
Randomランダムアクセスで開く
アクセスモード
Read読み込み専用
Wright書き込み専用
Read Wright読み書き可能
ロックモード
Shared他のプロセスから参照可能
Lock Read他のプロセスから読み込み負荷
Lock Write他のプロセスから書き込み負荷
Lock Read Write他のプロセスから読み書き負荷


アクセスモードとロックモードは省略できます。
モードがAppend・Binary・Output・Randomで、なおかつ指定されたファイルが存在しない場合には、自動的に新しくファイルが生成されます。

結構ゴチャゴチャしていますね。

ほとんどの場合は、アクセスモードやロックモードを気にする必要はありません。原則として、開くファイルのパスと、そのモード、そしてファイル番号さえ正しく指定されていれば問題ありません。先ほど載せたサンプルコードでも、アクセスモードやロックモードは省略しています。

さて、④の手順を経た時点で、既にファイルは開かれています。開かれたファイルに書き込んでいるのが⑤の部分です。Print ステートメントを使うと、指定されているファイル番号のファイルに、指定されたデータが書き込まれます。
今回の場合は変数 fString の内容がテキストファイルに書き出されるようになっています。

Print ステートメント

Print #ファイル番号, 書き出すデータ

さて、ファイル操作で忘れてはならないのが、最後の⑥の部分です。
ここでは Close というステートメントが登場しています。このステートメントは、指定されたファイル番号のファイルを閉じることができるステートメントです。一応、VBA の仕様としては、プロシージャの実行が終了した次点で、開かれているファイルは自動的に閉じられるようになっています。ただし、こういったファイル操作の基本として、開いたものは必ず明示的に閉じるのが原則です。開きっぱなしにして放置することはせず、必要がなくなった時点で必ず開いたファイルは閉じるように癖をつけておきましょう。

Close ステートメント

Close #ファイル番号


モードを指定して、任意のファイルを開き、書き込みそして閉じる。これがファイル操作の基本になります。

Open ステートメントは引数が多くて困惑してしまいがちですが、今回示したサンプルコードのように、最低限の引数を指定するだけで十分に活用することができるはずです。間違いやすい点としては、ファイルのパスを指定する部分。ここは一字一句間違えずに指定する必要がありますので、注意しましょう。


■まとめ

今回は、まだまだ基本的な部分にとどめていますが、それでも結構わかりにくかったのではないでしょうか。大体、いろんなモードがあるみたいだけど違いはなんじゃ? という感じになっている人も、実際結構いるんじゃないかと思います。

私も、初めてファイルを操作しようと思って調べたとき、なんじゃこりゃと思いました。いろいろなモードがあるし、第一どれを使えばいいのかわかりませんよね。

まずは、モードを Output にして、ファイルに文字列書き出すことから始めてみましょう。単純にファイルに文章を書き出したいだけなら、これで十分です。
このモードの場合は、その都度ファイルが上書きされます。もとから何か文字列が書かれているファイルであっても、Print ステートメントによって指定されたデータが新しく書き出されて保存されます。理屈としては非常に簡単ですね。

ファイル操作の世界はとても奥が深いです。慣れてくると、テキストだけでなく、様々なデータを扱うことが可能になりますし、データの暗号化など、ちょっとワクワクするような処理を実現することもできるようになります。まずは基本に忠実に、単純なことからしっかり基礎を固めましょう。応用はそのあとじっくりやっていけばいいのです。



■格言

ファイルを開くにはOpenステートメント
書き出すだけならOutputモードで


焦りは禁物。基礎こそしっかりマスターしましょう。







Chapter.118 [ テキストファイル操作:読み込み編 ]

■テキストファイルの読み込み

前回は、テキストファイルを操作するための、基本的な部分を解説しました。Open ステートメントの使い方や、引数の概略などを解説しましたね。また、開いたファイルを必ず閉じるなど、ファイル操作の原則についても簡単に触れました。この辺は、テキストファイルに限らず、あらゆるファイル操作の基本になる部分ですので、しっかり癖付けをしておいたほうがいいでしょう。

さて、今回はファイルの読み込みをやってみます。ファイルを作成してデータを保存することができても、そのデータを読み込んで、再活用できなければ意味がありません。データの読み出しをマスターして、テキストファイルの便利さを実感してもらえたらと思います。

早速ですが、まずはテキストファイルからデータを読み込むためのコードを見てみましょう。

Sub inport()

    Dim L As Long
    Dim fName As String
    Dim fString As String
    
    L = FreeFile
    fName = ThisWorkbook.Path & "\test.txt"
    
    Open fName For Input As #L '①
    Input #L, fString '②
    
    Debug.Print fString
    
    Close #L
    
End Sub

前回解説したように、ファイルを開くためには Open ステートメントを使います。①の部分で、テキストファイルを開いているのがわかりますね。ただし、この時点ではただ単にファイルが開かれているだけです。まだ、データを読み込んではいません。

②の部分、ここが今回の肝です。
ここでは、Input ステートメントを使って、開いたファイルからデータを読み込んでいます。変数 fString には、読み込まれたデータが格納され、イミディエイトペインにそれが打ち出されるようになっています。

これで、ひとまずは読み込みができましたね。しかしこのままでは、とても実用的とは言えません。なぜなら、たくさんの問題点が潜んでいるからです。次からはこれについて考えてみましょう。


■複数行のデータ

さて、先ほど載せたコードで、とりあえず読み込み処理を行うことはできました。しかし、大きな問題が実は潜んでいます。それは、読み込みたいデータが複数行に渡っていた場合です。

例えば、次のような内容のテキストファイルがあったとします。

テキスト
ファイル

全部で2行のデータがありますね。
これを先ほどのコードで読み込んでみると、イミディエイトペインには次のように出力されます。

テキスト

あれ、おかしいですね。データは2行あるはずなのに、データが1行分しか読み込まれていません。たった1行で用の済む場合はそれほどないはずで、複数行のデータが読み込めないとすると、今後結構困った問題になりそうです。

実は、Input ステートメントは、データを読み込む際の受け入れ先に複数の変数を指定することができます。例えば、2行のデータからなるテキストファイルを正しく読み込むには、次のようにすればうまくいきます。

Sub inport2()

    Dim L As Long
    Dim fName As String
    Dim fString1 As String
    Dim fString2 As String
    
    L = FreeFile
    fName = ThisWorkbook.Path & "\test.txt"
    
    Open fName For Input As #L
    Input #L, fString1, fString2
    
    Debug.Print fString1 & fString2
    
    Close #L
    
End Sub

Input ステートメントは、複数の引数を受け取ることができます。カンマで区切って複数の変数を指定すれば、1行目と2行目のデータが順番に格納され、イミディエイトペインに出力されます。

ただし、出力される結果を見ると、なかなか興味深いことが起こります。先ほどのように、1行目に「テキスト」2行目に「ファイル」と書かれたテキストファイルを、上記のコードで読み込んでからイミディエイトペインに出力すると、次のように表示されます。

テキストファイル

おかしいですね。2行のデータを読み込みしたはずなのに、出力結果は1行になりました。

このことから、Input ステートメントについて、次のように解釈することができます。

Input ステートメントの仕組み
・Input ステートメントはデータを読み込むことができる
・データは1行分ずつ読み込まれる
・引数をカンマ区切りで指定し、複数行のデータを読み込むこともできる
・読み込まれたデータには、改行が含まれない

Input ステートメントでは、データが1行ずつ読み込まれます。そして、複数行のデータがファイル内に存在する場合、複数の変数を用意しておけば個別に読み込むこともできるわけです。しかし、考えてみると、微妙に使いにくいです。なぜだかわかりますか?

テキストファイルが複数行に及ぶ場合、その行数分の変数を用意しなければならないというのはきついです。開くファイルがどのような構造で、何行あるのかわかっている場合は問題ないかもしれません。しかし、実際には、何行のデータがあるのかわからない場合がほとんどでしょう。先に行数がわかっていなければならないとなると、かなり使い道が限定されてしまいます。

これは困りましたね。


■何回も呼び出してみる

さて、Input ステートメントを使って読み込み処理ができる、というところまではよかったのですが、いろいろな問題点が浮き彫りになってきました。
1行だけのデータを読み込むだけなら簡単でした。しかし、データが複数行になった途端、困ったことが目白押しです。なんとか、これらの問題点を解決できないでしょうか。

Input ステートメントを使って複数行のデータを読み込む際には、カンマを使って複数の変数を用意するよりも、優れた方法というのが実は存在します。それは、Input ステートメントを何回も呼び出す、という方法です。

Sub inport3()

    Dim L As Long
    Dim S As String
    Dim fName As String
    Dim fString As String
    
    L = FreeFile
    fName = ThisWorkbook.Path & "\test.txt"
    
    Open fName For Input As #L
    
    Do
        Input #L, S '①
        fString = fString & S & vbCrLf
    Loop Until EOF(L) '②
    
    Debug.Print fString
    Close #L
    
End Sub

上記のコードでポイントとなるのは2点です。

まず①の部分を見てください。
ここでは、Input ステートメントを使って、変数 S にデータを読み込んでいます。そしてその下の行では、一度変数 S に読み込んだデータを、変数 fString に連結していますね。この部分は繰り返し処理 Do~Loop 文の中ですから、何度も呼び出されることになります。

先ほど解説したとおり、Input ステートメントでは改行が無視されてデータだけが抽出されます。文字列を連結する際に vbCrLf という定数を一緒に連結することで、しっかり改行が挿入されます。vbCrLf は改行と同じ意味のある定数で、これをデータの末尾に毎回挿入することで、テキストファイルの内容と同じ場所に改行が入るようになるというわけです。

また、Input ステートメントは、一度読み込んだ行のデータは二度と読み込みません。1行目、2行目、3行目……という具合に、次々と連続で読み込んでいきます。ですから、今回のような繰り返し処理の中で呼び出すと、行数が複数であっても、自動的に、順番に、データが読み込まれていきます。

さて、続いて②を見てください。ここでは見慣れないステートメントが出てきていますね。
ここで登場している EOF というステートメントは、ファイルの末尾を判断してくれる便利な関数です。引数には、ファイルを開いた際に使ったファイル番号を指定します。今回の場合は変数 L にファイル番号を取得してから開いていますので、それをそのまま EOF 関数に渡しています。

EOF 関数は、引数で指定されたファイル番号のファイルが、どこまで処理されたのかを調べます。その上で、ファイルの末尾にまで処理が達している場合は True を、まだ途中の場合は False を返してきます。
繰り返し処理の終了条件に EOF 関数を使うことで、ファイルの読み込み処理が末尾に達した時点で、自動的に繰り返し処理から抜けるようになるわけですね。

この EOF 関数は、ファイルの処理では必ずといっていいほど登場します。ファイル操作の基本と言っても過言ではない関数ですので、しっかりとその働きを覚えておきましょう。


■まとめ

さて、テキストファイルの読み込みについて、ざっくりと理解できましたでしょうか。
今回解説した Input ステートメントは、様々な手法でデータを読み込むことができる奥の深いステートメントです。
変数を複数用意して、それぞれを個別に1行ごと読み込ませることもできますし、繰返し呼び出すことで、順番に1行ずつ処理することもできるのでしたね。繰返し呼び出す手法を用いる場合には、EOF 関数が役に立ちます。この関数を知っているかどうかで、ファイルの読み込みについては大きな差が出ますね。

前回、そして今回の講座の内容をある程度理解できれば、単純なテキストファイルの読み書き程度のことはできるようになったと言えます。ファイルを開き、書き込み、閉じる。そしてそのファイルを開き、読み込み、閉じる。これこそがファイル操作の基本です。

しかし、今回紹介した Input ステートメントを用いた処理では、全てのファイルを正しく処理できるとは限りません。実は、ある特殊なケースでは、今回紹介した内容をそのまま使いまわすことができないのです。ま、そのあたりは次回以降、じっくり解説していきたいと思います。まずは今回までの内容をしっかり習得しておきましょう。



■格言

読み込みにはInputステートメント
目的に応じて処理方法を選択する
EOF関数をうまく利用する


いろいろなテキストファイルで、読み込みが正しくできるか試してみるのもいいでしょう。なにか発見があるかもしれません。







メールフォーム

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

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

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

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ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。