ORANGE pico で10秒連打ゲームを作ってみた。
10秒間でのゲームコントローラの連打数を測定するのに加え、1秒ごとそれぞれの連打数も測定する。
プログラム
10 x=button(-1):pb=0:yoffset=6
20 cls:clt:pt=0:count=0:curcnt=0:locate 0,yoffset-2:print "READY?";
30 t=tick():b=button(0)
40 if pt<60 && 60<=t then locate 7,yoffset-2:print 3;
50 if pt<60*2 && 60*2<=t then locate 9,yoffset-2:print 2;
60 if pt<60*3 && 60*3<=t then locate 11,yoffset-2:print 1;
70 if pt<60*4 && 60*4<=t then locate 13,yoffset-2:print "GO!";:locate 0,yoffset:print "0 0";
80 if 60*14<=t then locate 0,yoffset+11:print "TOTAL ";count;:goto 140
90 if 60*4<=pt && pt/60<>t/60 then locate 0,yoffset+t/60-4:print t/60-4;" 0";:curcnt=0
100 pt=t
110 if b<0 then goto 30
120 if 60*4<=t && !(pb & 16) && (b & 16) then count=count+1:curcnt=curcnt+1:locate curcnt+1,yoffset+t/60-4:print chr$(&H8F);curcnt;
130 pb=b:goto 30
140 locate 0,yoffset+13:print "Press START to retry."
150 b=button(0)
160 if b>=0 && (b & 1024) then pb=b:goto 20
170 goto 150
このプログラムは、CC0 1.0 でライセンスする。
操作方法
ゲームコントローラを接続して実行する。
- Aボタン:連打
- STARTボタン:終了時のリトライ
実行結果例
解説
10行目:全体初期化
ボタンの初期化と描画位置の設定を行う。
20行目:ラウンド初期化
画面と状態(カウント・時刻)の初期化を行う。
30行目:状態取得
時刻とボタンの状態を取得する。
40~90行目:描画
時刻に応じて描画を行う。
- 1秒、2秒、3秒:カウントの更新
- 4秒:カウントの更新と、最初の1秒用のグラフの最初の描画
- 5~13秒の整数秒:グラフの最初の描画
- 14秒:合計連打数の描画、ゲーム終了
100~130行目:状態反映
- 100行目:現在時刻を更新する
- 110行目:ボタンの状態の取得に失敗している場合、処理をスキップする
- 120行目:測定開始後、かつAボタンが新たに押された場合、カウントを増やしてグラフを描画する
- 130行目:現在のボタンの状態を更新する
140~170行目:ラウンド終了
メッセージを表示し、STARTボタンが押されるまで待機する。
STARTボタンが押されたら、ラウンド初期化に戻り、次のラウンドを開始する。
今回新たに発見した罠
ボタンの状態の取得に成功するとtickms()の時刻が遅れる
今回のプログラムを作成するにあたり、最初は tick()
ではなく tickms()
を使用して時刻を取得していた。
すると、1秒間隔のはずのカウントが約2秒間隔と、想定より時間がかかっていた。
tick()
に切り替えたところ正常に動作したので、原因は tickms()
が想定通りに動いていないことであると考えられる。
そこで、以下の仮説を立てた。
- I2C通信を行うと
tickms()
の時刻が遅れる - ボタンの状態の取得を試みると
tickms()
の時刻が遅れる - ボタンの状態の取得に成功すると
tickms()
の時刻が遅れる
まず、I2C通信と tickms()
の関係を調べるため、以下のプログラムを実行した。
これは、標準搭載のEEPROMの適当な場所を読み込むプログラムである。
さらに、1秒ごとに tick()
の値と tickms()
の値を出力する。
10 clt:cltms:toprint=60
20 t=tick():m=tickms()
30 x=i2cr(2,&H50,256,1)
40 if toprint<=t then print t;" ";m:toprint=toprint+60
50 if t<300 then goto 20
以下の出力が得られた。
60 986
120 1981
180 2982
240 3976
300 4970
tick()
の時刻に比べて tickms()
の時刻は少し遅れているが、あまり差は大きくない。
とはいえ、少しずつ差が大きくなっているようであり、影響が全く無いとはいえない。
ここで、上のプログラムの30行目を以下に差し替え、I2C通信で読み込む量を増やしてみた。
30 x=i2cr(2,&H50,8000,1)
すると、以下の出力が得られた。
65 1071
129 2143
181 3000
245 4072
310 5143
やはり少しずつ差が大きくなっているものの、あまり差は大きくない。
次に、プログラムを以下に変更し、ゲームコントローラのボタンの状態を取得するようにした。
10 y=button(-1):clt:cltms:toprint=60
20 t=tick():m=tickms()
30 x=button(0)
40 if toprint<=t then print t;" ";m:toprint=toprint+60
50 if t<300 then goto 20
ゲームコントローラを接続せずに実行すると、以下の出力が得られた。
60 989
120 1986
180 2983
240 3980
300 4977
きれいに差が3(ms)ずつ広がっているが、やはり差は大きくない。
ゲームコントローラを接続して実行すると、以下の出力が得られた。
60 526
120 1077
180 1693
240 2143
300 2666
tickms()
の返り値が想定の約半分と、大幅に小さくなっている。
すなわち、ゲームコントローラのボタンの状態の取得に失敗した場合の tickms()
への影響は無いまたは少ないが、取得に成功すると tickms()
に大きな影響が出ることが読み取れる。
対照実験のため、繰り返す処理をI2C通信を行わない処理に差し替えてみた。
10 clt:cltms:toprint=60
20 t=tick():m=tickms()
30 x=sin(1)
40 if toprint<=t then print t;" ";m:toprint=toprint+60
50 if t<300 then goto 20
以下の実行結果が得られた。
60 987
120 1984
180 2981
240 3977
300 4974
I2C通信を行わなくても、I2C通信を行ったときと同程度の tickms()
の遅れが生じたことがわかる。
よって、一般のI2C通信では tickms()
への影響はみられないと考えられる。
これまでの調査で、pause
コマンドを実行すると tickms()
のカウントが止まることがわかっている。
さらに、ゲームコントローラのボタン状態の取得に成功した場合は、約10msかかり、そのうちほとんどの時間で通信を行っていない。
この通信と通信の間の時間において、内部で pause
コマンドと同様の処理が行われ、tickms()
のカウントが止まっている可能性が考えられる。
この場合、ボタンの状態の取得に成功しなくても、最初の 0x00
の送信にだけ成功すれば、待機処理が行われ、tickms()
の遅れが生じる可能性が考えられる。