第3回 画像でアプリをグレードアップ「5.同じ部分はまとめよう」

3−5 同じ部分はまとめよう

 さて、3つのボタンに割り当てられたコードを並べてみてみましょう。(ちょっと、表示が横にはみ出ているかも知れませんね。)

ぐーちょきぱー
Sub object1014()

 Dim mode_Palm as Integer
           
 mode_Palm = rand()*3+1
          
 count_Game = count_Game + 1
              
 DrawBitmap 1010+mode_Palm,8,20
 DrawBitmap 1008,88,55
            
 Lbl1018.Label=Mid("EvenWin!Lose",mode_Palm*4-3,4)

 If mode_Palm=2 Then
     count_Win = count_Win + 1
 End If

 Lbl1017.Label=Format(count_Win/count_Game * 100,"##0.0%")

End Sub
Sub object1015()

 Dim mode_Palm as Integer
            
 mode_Palm = rand()*3+1
          
 count_Game = count_Game + 1
              
 DrawBitmap 1010+mode_Palm,8,20
 DrawBitmap 1009,88,55
            
 Lbl1018.Label=Mid("LoseEvenWin!",mode_Palm*4-3,4)

 If mode_Palm=3 Then
     count_Win = count_Win + 1
 End If

 Lbl1017.Label=Format(count_Win/count_Game * 100,"##0.0%")    
    
End Sub
Sub object1016()

 Dim mode_Palm as Integer
            
 mode_Palm = rand()*3+1
          
 count_Game = count_Game + 1
           
 DrawBitmap 1010+mode_Palm,8,20
 DrawBitmap 1010,88,55
            
 Lbl1018.Label=Mid("Win!LoseEven",mode_Palm*4-3,4)

 If mode_Palm=1 Then
     count_Win = count_Win + 1
 End If

 Lbl1017.Label=Format(count_Win/count_Game * 100,"##0.0%")    
    
End Sub

 これらの3つを比べると、赤い部分以外は全く同じ事に気が付きますね。
そう、プログラムを作る上で、こういう重複する部分は、できるだけ1つにまとめるのが原則です。
同じような部分が複数存在すると、修正する時に困難だからです。
例えば、極端ですが、ジャンケンのルールが変わってしまった場合、3つのコードを修正しなければなりません。ま、今回の程度であればそれほど大変な作業ではありませんが、それでも面倒です。
では、これらの3つから、1つの共通で使えるコードにまとめてみましょう。同じでない部分(赤い部分)は3箇所ありますので、順に見ていくことにします。

まず、自分の手を表示する部分、

    DrawBitmap xxxx,88,55

ですが、ここは、サイコロやPalmの手を表示する方法と同じ手法が使えますね。
自分の手をPalmの手と同様に、mode_myという変数を用意して入れておくことにしましょう。
すると、この部分は、

    DrawBitmap 1007+mode_my,88,55

です。それほど難しくありませんでしたが、自分の手を識別するために、変数mode_myが必要でしたね。

次の部分は、いわば勝ち負けの判定部分です。mode_myとmode_Palmと勝ち負けの関係は次のようになっています。(○:勝ち、×:負け、−:あいこ)

mode_Palm
123
mode_my 1 ×
2 ×
3 ×

これをできるだけ簡単にすれば良いわけですが、簡単にするためには頭を使う必要があります。
色々な方法がありますが、今回はIf文やSelect Case文などの条件判断をしなくても良いように工夫してみました。
(この工夫がプログラミングの楽しさですね)
上の表を縦に展開すると、このようになります。

mode_mymode_Palm判定
11
12
13×
21×
22
23
31
32×
33

これから、勝ち負けの関係を見つければ良いのですが、どうも、難しそうなので、少し、計算式を考えてみます。
「(mode_my)×3 + mode_Palm −3」という式で計算してみると、

mode_mymode_Palm判定mode_my*3+mode_Palm-3
111
122
13×3
21×4
225
236
317
32×8
339

見事、連番になりましたが、このように、縦横の表から連番にしたり、その逆をすることは、良く使う手法です。
連番になったらMid()が使えますので、勝ちを1、負けを5、あいこを9として、
    Dim f as Integer
    
    f = Val(Mid("915591159",mode_my*3+mode_Palm-3,1))
    Lbl1018.Label=Mid("Win!LoseEven",f,4)
としてはどうでしょうか?最初のMid()内の"915591159"がミソですね。勝ち負けあいこを、1,2,3にしても良いのですが、その後のMid()で4文字の文字列を取り出すことを考えると、この方が便利ですね。

おかげで、最後の相違点は、
    If f = 1 Then
        count_Win = count_Win + 1
    End If
とまとめることができます。

さて、これら1つにまとめたコードは、どうやって使えば良いのでしょうか?
変数であれば、Globalコマンドで定義して、プログラム中のどこからでも使うことができました。
このようなプログラムの部品(ルーチンと言います)の場合は、共通モジュールとして定義する必要があります。
共通モジュールは、1つのファイルとして保存される部品で、ちょうど、画像ファイルの読み込みに似ています。

モジュールは、IDEのプロジェクトエクスプローラの一番下「Modules」に追加(Add)します。
画像と違って、予め用意しなくても[Add New Module]を選択して、IDE上で新規に作成することができます。

    

新しいモジュールを追加すると、次のように表示されます。
    Sub <substitute your Sub name here>()

    End Sub
変数と同じように、共通モジュールにも名前が必要で、この名前を指定して、他のルーチン内から呼び出します。
今回の名前は、Exec_JanKenという名前にしておきましょう。名前を決めたら、次のように<>部分を名前で置き換えます。
    Sub Exec_JanKen()

    End Sub
さて、勝敗の判定や各種表示など、共通化できる部分はここにコード化して記述すればよいのですが、コード内では作り出せない値があることに気付くでしょうか?それは、自分の出す手、つまり変数mode_myにセットされる値で、この値だけは、このルーチンに外から渡さなければなりません。
このように、外部との連絡をとる役割をする値を「引数(ひきすう)」と言います。引数は、使う時に次のように宣言します。
    Sub Exec_JanKen(mode_my as Integer)

    End Sub
カッコ内に、Dimなどで宣言するのと同じような方法で定義します。これで、このサブルーチン内で、mode_myという変数を使うことができます。
後は、共通で使えるコードを記述するだけです。
    Sub Exec_JanKen(mode_my as Integer)

        Dim mode_Palm as Integer
        Dim f as Integer
            
        mode_Palm = rand()*3+1
          
        count_Game = count_Game + 1
                  
        f = Val(Mid("915591159",mode_my*3+mode_Palm-3,1))
    
        If f=1 Then
            count_Win = count_Win + 1
        End If

        DrawBitmap 1010+mode_Palm,8,20
        DrawBitmap 1007+mode_my,88,55
        Lbl1018.Label=Mid("Win!LoseEven",f,4)
        Lbl1017.Label=Format(count_Win/count_Game * 100,"##0.0%")

    End Sub
変数の宣言を最初に、表示を最後にまとめてみましたが、これで共通なルーチンの完成です。
共通モジュールのコードウィンドウを閉じる時に、保存する先を尋ねられますが、この共通モジュールが画像ファイルのような独立した1つのファイルであることがわかりますね。

一方、呼び出す側は、Callコマンドを使って呼び出します。

    Call サブルーチン名

ですので、例えば、グーの時は、
    Sub object1014()

        Call Exec_JanKen(1)
    
    End Sub
とするだけです。()内に引数を入れてCallしますが、チョキなら Call Exec_JanKen(2)、パーなら Call Exec_JanKen(3)になりますね。ルーチン名は、変数同様、大文字小文字の区別なしです。
これで、各ビットマップオブジェクトのイベントルーチンは1行になり、共通部分を1つにスッキリまとめることができました。

この共通モジュールは、1つのファイルとして、どのプログラムからでも組み込んで使うことができます。しかし、決まった番号のビットマップや、Lbl1017などの名前がついたラベルが画面上に配置されていないと使えないような作りになっているので、あまり汎用的とは言えません。本当に汎用的なモジュールであれば、どのようなプログラムを作っても再利用できるように設計します。
とりあえず、共通モジュールを使った方法を紹介しましたが、NS Basicでは、もうすこし別の方法で共通化することが可能です。
その方法については、次回に説明したいと思います。

今回は、画像の貼り方と共通モジュールについて、また、重要な条件分岐のSelect Caseを紹介しました。
ここまでできれば、NS Basicを使って、かなり面白いアプリケーションが作れるのではないでしょうか?
一応、画像が表示されていれば、ジョークソフトでもかなり立派なアプリケーションに見えますね。

ご質問、ご要望、ご感想、などなど、メールか掲示板でお送り下されば幸いです(筆が進みます)

前へ    目次へ

第3回 画像でアプリをグレードアップ「5.同じ部分はまとめよう」