第11回 何か作ってみよう「5.ムシムシ大行進」

11−5 ムシムシ大行進

 さて、大まかな準備が揃ったところで、実際のコードを書いてみましょう。え、フローは、どうするの?って・・・
先ほど、仮に出しましたね、全体像の予想図

    

これを元にフローを考えながら、プログラムも書いちゃいましょう。とても実践的なのですが、教育的意義の低い方法です・・・

まず、ムシの表示と移動をセットで進めましょう。表示と移動は、定番「消す処理と描く処理がセット」になっていますので、実際にムシの位置を表す座標は、表示用と消去用の2種類を用意しなければなりません。
これらと方向を表す変数を、次ようにStartup()で用意することにしましょう。
    '虫の座標(表示用)
    Global X1 as Integer
    Global Y1 as Integer
    
    '虫の座標(消去用)
    Global X2 as Integer
    Global Y2 as Integer

    '虫の向き、右、下、左、上=1,2,3,4
    Global M1 as Integer

    '初期値
    '虫の向き 右(=1)    
    M1=1
まず、移動の定番は、FormのAfter()に
    DrawBitmap 1004,X2,Y2        '前の虫の消去
    DrawBitmap 1005+M1,X1,Y1     '新しい位置の虫を描画

    '今の座標を保存
    X2=X1
    Y2=Y1
こんな始まりですね。これを方向によって移動させるわけですから、例えば、
    '新しい位置へ移動
    Select Case M1
        Case 1     '右
             X1=X1+10
        Case 2     '下
             Y1=Y1+10
        Case 3     '左
             X1=X1-10
        Case 4     '上
             Y1=Y1-10
    End Select
これで、移動は可能なんですが、なにか一つスッキリしませんね。私だけでしょうか?
1つは、それほど大げさな処理でもないのに、Select Caseが大きな顔をしているのが気に入りません。そこで、こう言う場合、方向に対する移動量を配列変数にします。???何を言っているの?とりあえず、ソースを見て下さい。
まず、Startup()に次のような配列変数と初期値を用意します。
    'ムシの移動量(向き対応)
    Global Hx(4) as Integer
    Global Hy(4) as Integer
    
    '移動量のセット
    Hx(1)=10
    Hx(3)=-10
    Hy(2)=10
    Hy(4)=-10
この新たに登場した「ムシの移動量」を使えば、先ほどの移動処理が、次のようにシンプルになります。
    '新しい位置へ移動
    X1=X1+Hx(M1)
    Y1=Y1+Hy(M1)
もちろん、初期値を与えていないHx()、Hy()は、0 のままです。例えば、右方向への移動では、Hx(1)=10、Hy(1)=0ですので、縦方向の座標は変化しないわけです。結構スッキリしたのではないでしょうか?

さて、先に進んで、壁にぶつかった処理です。
こちらは、移動した先が壁だったら、向きを変えれば良いわけですから、難しくなさそうです。
例えば、
    'もし移動した先が壁だったら、移動前の位置に戻して方向反転する
    If Maze(X1/10+1,Y1/10+1)=1 Then
        '移動前の位置に戻す
        X1=X2
        Y1=Y2

        '向きを変える
        M1=M1+1

        '向きが4を越えたら、1にする
        If M1>4 Then
            M1=1
        End If
    End If
気を付ける点は、Maze(i,j)の添字i、jです。ムシの実際の座標(X1,Y1)は、10ピクセルずつ増減しますので、0〜150になりますが、Maze(i,j)は、i,j=1〜16で定義されています。したがって、(X1,Y1)をMaze()の座標系に変換しなければなりません。それが Maze(X1/10+1,Y2/10+1) の部分ですね。

表示、移動、ぶつかった処理が揃ったところで完成、と言いたいところですが、忘れている処理があります。それは、外周です。
このままだと、ムシは、Palmの画面からはみ出して、見えなくなってしまいます。いや、たぶん、出た後すぐに、Maze()の添字がオーバーしますので、PalmOSが致命的なエラーで止まるんじゃないでしょうか?そうならないために、はみ出さないようにチェックしなければなりません。色々な方法がありますが、ここでは素直に外周をチェックすることにしましょう。

素直な、外周チェックは、結構簡単です。条件としては、
  • X1が150を超えた
  • X1が0より小さくなった
  • Y1が150を超えた
  • Y1が0より小さくなった
大きい方は、本来は159を超えたらですが、10ピクセル毎の移動ですから、150で良いですね。
さて、この条件を考えてコード化すれば良いので、例えば、右端を超えたときは、
    '右端を超えたら方向転換する
    If X1>150 Then
        '横方向を移動前の位置に戻す
        X1=X2

        '向きを変える
        M1=M1+1

        '向きが4を越えたら、1にする
        If M1>4 Then
            M1=1
        End If
    End If
これを先ほどの条件に合わせて4つ作るだけですが、やっぱりスッキリしませんね。
スッキリしないのは、どこか、というと、
  • 向きを変える処理は、同じに処理なのに、個々のIf文に入っているのが無駄
  • 外周が4つあるが、実際は、もっとまとめられそうな気がする
そうですね、まず、向きを変える処理を共通化してみましょう。
例えば、次のように考えてみます。
    '方向転換フラグの用意
  Dim flgTurn as Integer

    Do  
        '方向転換フラグの初期化
        flgTurn = 0
        
        '横方向にはみ出しているかチェック
        'はみ出していたら、戻して方向反転を有効にする
        If X1>150 Then
            X1=X2
            flgTurn=1
        End If
        
        If X1<0 Then
            X1=X2
            flgTurn=1
        End If

        '縦方向にはみ出しているかチェック
        'はみ出していたら、戻して方向反転を有効にする
        If Y1>150 Then
            Y1=Y2
            flgTurn=1
        End If
        
        If Y1<0 Then
            Y1=Y2
            flgTurn=1
        End If

        'もし移動した先が壁だったら、移動前の位置に戻して方向反転する
        If Maze(X1/10+1,Y1/10+1)=1 Then
            '移動前の位置に戻す
            X1=X2
            Y1=Y2
            flgTurn=1
        End If
        
        '方向転換フラグが1だったら、方向転換
        If flgTurn=1 Then
            '向きを変える
            M1=M1+1
            
            '向きが4を越えたら、1にする
            If M1>4 Then
                M1=1
            End If
        End If        
    Loop 
向きを変える処理部分だけ取り出して、そこを実行するか否かは、方向転換フラグflgTurnという変数を用意して、判定しています。これで、同じ処理を繰り返さなくても大丈夫になりました。
さて、もう1つの無駄、これは、このコードを見ればわかりますね。
もう、詳しくは言わなくてもよいでしょう、
         If X1>150 Or X1<0 Then
これだけヒントを出しておけば大丈夫ですね。

さて、今まで出てきた部分を「パズル」のように組み合わせてください。1つのムシ移動プログラムが出来あがります。
でも、まだ、実行しないで下さい!!。わからない人は、リセットピンを用意して、実行してみましょう。

最後の仕上げは、ブロックを置く処理と結合させる事です。
今回のプログラムは、2つの処理がそれほど関係していない、という前提で作り始めましたので、結合もそれほど難しくないでしょう。
ただ、1つ考慮する点は「どの時点でイベントを発生させるか?」という点です。
FormのAfter()モジュール内で、Do〜Loopなどの連続的な処理をする場合、SysEventAvailable()で意図的に抜け出さないと、リセットが必要な無限ループにんなる事は、周知の事実か、または、数分前に経験したかと思います。例えば、このプログラムで、定石通りに、

    Do Until SysEventAvailable()=1

とすると、流れ的には、

    

ムシの移動やぶつかった処理が終わった後、新しい座標に表示される前に、ループを抜けてしまいます。したがって、ブロックを置く位置に、実は、ムシが表示される予定だったかもしれません。ということで、これでは感覚的にも、精神衛生的にも、よくありませんので、実際の表示が終わった後にループを抜けるのが妥当でしょう。したがって、フローは次のように考えるのが妥当でしょう。

    

コードは、サービスで掲載しておきましょう。
        If SysEventAvailable()=1 Then
            Exit Do
        End If

一通り、コード類の部品を紹介してみました。
多分、頭の中でフローも組み立てられている事と思いますので、後、完成までは、パズルのようですが各自で組み上げてみてください。もう、ここまで来れば、大丈夫でしょう?

あ、一つだけ、載せていないコードがあります。
もし、ムシの移動が早すぎるようでしたら、Delayコマンドをどこかに入れて時間調整して下さい。

戻る     目次へ     次へ

第11回 何か作ってみよう「5.ムシムシ大行進」