第6回 Palmに動作を伝えよう 〜 イベントのお話 〜「3.画面をタップ その2」

6−3 画面をタップ その2

先ほどは、ペンでタップする使い方として、直接タップした座標を得て使いました。これは、GetPenコマンドで得られた座標をそのまま使えば良いわけですから簡単ですね。
次は、応用編としてこれを仮想的な座標と連携させて使ってみようと思います。
???何の事を言っておるのだ?と思われるかもしれませんが、マインスイーパやオセロ、はたまた、AmiSolのように、画面上に何らかのマス目があって、その中をタップするとそのマスが選択されるような動作ですを目指します。
これは、実際の画面が160x160であるのに、その上に10x10のマス目を描いて処理するような使い方ですが、どうして、「座標系の変換」などと難しそうなことが出てくるのか?いう疑問が沸いてきますね。

その疑問を解消するため、逆に、その他の方法で実現することを考えてみましょう。
例えば、オセロ(R)は、縦横8x8=64マスのマス目で構成されています。この各マスをタップしたらコマを置くわけですから、オブジェクトを使うとなれば、64個のオブジェクトが必要になりますね。

    

これは、現実的にかなり苦しいです。ちょっとした画面デザインの変更も大変です。
また、多くのオブジェクトを貼り付けることは、プログラムに負荷をかけますので、オブジェクト数はできるだけ少ない方がプログラム的にも良いのです。
したがって、そのような方法はプログラム的に不適ということになります。
(ちなみに、Visual Basicなどでは、オブジェクト自体が配列になりますので、少ない数ならオブジェクトでも作ることができます。)

では、次のような画面を考えて、そのマス目をタップしたら黒く塗りつぶす、というプログラムを考えてみましょう。

    

1つのマス目は、14x14ピクセルです。実際は、上下左右に1ピクセルずつの枠線がありますので、1枠は15x15ピクセルとみなして良いでしょう。
これが8x8で64マスあり、幅や高さは、15x8+1=121ピクセルになりますね。最後の+1は、先頭もしくは最後のマスの枠線の分です。
さて、座標の左上は、(160-120)÷2=20、縦方向は、160-120-10=30、にしましょう。

    

さて、タップしたマス目を塗りつぶすために必要なのは、塗りつぶす枠の左上の座標と塗りつぶす大きさですね。
塗りつぶす大きさは、マス目の大きさと等しいわけですから、これは、14x14で良いでしょう。
問題は、左上の座標です。

これは、このように考えます。
まず、マス目の座標(X',Y')から画面の座標(X,Y)に変換することを考えて見ます。
マス目の座標(0,0)〜(7,7)と画面の座標(0,0)〜(159,159)の関係は、
      X=15*X'+21 ------------------(1-1)
      Y=15*Y'+31 ------------------(1-2)
になりますね。これは大丈夫でしょう。
逆に、画面の座標をマス目の座標に変換するには、この式を変形して、
      X'=(X-21)/15 -----------------(2-1)
      Y'=(Y-31)/15 -----------------(2-2)
ただし、X'やY'は、小数の値を含みませんから、
      X'=Floor((X-21)/15) ------------(2-3)
      Y'=Floor((Y-31)/15) ------------(2-4)
と整数化しておけば良いでしょう。
(通常のBASICでは整数化にInt()を使いますが、ここではFloor()を使いました。これらの詳細はBASIC講座2−3をご覧下さい。)

これらの式をまとめると、タップした座標(X,Y)からマス目の座標(X',Y')を求めて、その座標を画面の座標(X,Y)に変換することができます。
具体的には、式(1-1)と式(2-3)、式(1-2)と式(2-4)をまとめます。
      X=15*Floor((X-21)/15)+21 -------(3-1)
      Y=15*Floor((Y-31)/15)+31 -------(3-2)
となります。ちょっと、わかりにくいかもしれませんが、式(2-3)を式(1-1)に代入しただけです。

これをプログラムに組み込んでみると、
[After]
    DrawBitmap 1005,20,30

[events]
    Dim X as Integer
    Dim Y as Integer
    Dim P as Integer
    Dim Q as Integer
        
    If GetEventType()=nsbPenDown Then
        GetPen X,Y,nsbPenDown
        P=15*Floor((X-21)/15)+21
        Q=15*Floor((Y-31)/15)+31
        FillRectangle P,Q,14,14,0,nsbNormal
    End If
となり、マス目をタップしたらそこが黒く塗りつぶされます。
まあ、これは、座標がはみ出ていることをチェックしていませんので、マス外でも黒く塗りつぶされますが、まずは、ここまでよろしいでしょうか?

このような仮想座標は、配列変数と組み合わせるとプログラムとして実用性が増してきます。
[Startup]
    Global M(8,8) as Integer

[After]
    DrawBitmap 1005,20,30

[events]
    Dim X as Integer
    Dim Y as Integer
    Dim P as Integer
    Dim Q as Integer
    
    If GetEventType()=nsbPenDown Then
        GetPen X,Y,nsbPenDown
        P=Floor((X-21)/15)
	Q=Floor((Y-31)/15)
        If P*(P-7)<=0 and Q*(Q-7)<=0 then
              If M(P+1,Q+1)=1 Then
                  FillRectangle 15*P+21,15*Q+31,14,14,0,nsbInverted
                  M(P+1,Q+1)=0
              Else
                  FillRectangle 15*P+21,15*Q+31,14,14,0,nsbNormal
                  M(P+1,Q+1)=1
              End if
        End If
    End If
このプログラムでは、マス目をタップする毎に、マス目の塗りつぶしと非塗りつぶしが順に切り替わります。
本来なら、FillRectangleの描画オプション、nsbInvertedなどを変数に代入してスッキリさせようと思ったのですが、うまく動かなかったので、こんな形にしました。
ポイントはM()という変数で、M(i,j)は、i=1〜8、j=1〜8で、マス目に対応しています。NS Basicの配列変数は要素 1から始まるので、1〜8ですが、ここにマスの状態 塗りつぶされている=1、塗りつぶされていない=0、が入っています。
タップされた座標から、この変数の要素を求めて状態を変化させ、同時に画面への描画を行っています。

このように、配列変数と仮想座標の組み合わせは、難解ですが、使い方次第でかなり便利になります。ここは、一度、式を分解して自分のモノにしておいてください。

さて、1つ気がついた方も見えるかもしれません。
        If P*(P-7)<=0 and Q*(Q-7)<=0 then
ですね。変な条件式です。
通常なら
        If P>=0 and P<=7 and Q>=0 and Q<=7 Then
とします。
しかし、今回は、条件を4つも並べるのが嫌だったので、2つにしてみました。原理的には、簡単な数学です。
      f(x)=(x-a)(x-b)
という2次関数(a<b)が、a≦x≦bである条件は、
      f(x)≦0
ですね。グラフにすると、

    

という状態です。

先ほどの式に戻ると、
      P*(P-7)<=0
が成り立つのは、0≦P≦7の時で、Pは整数ですから、0,1,2,3,4,5,6,7の何れかの場合のみ成り立ちます。
Qも同様ですね。
これは、昔、Ifの中で、ANDやORが使えなかった時のテクニックですから、今は使い道がないかもしれませんね。ま、こう言う方法もあることを知っておく程度で良いと思います。また、その他には、Mid()を使っても良いかもしれませんね。

画面のタップは、ペンの状態とその座標を得ることができるだけなので、直感的に理解しやすいイベントですが、応用次第ではボタンなどのオブジェクトを使わないでアプリを作ることが可能です。ここは、皆さんでいろいろと試してください。

前へ     目次へ     次へ

第6回 Palmに動作を伝えよう 〜 イベントのお話 〜「3.画面をタップ その2」