ワンポイントテクニック No.008

同時キー入力判定をする


 ご存じのようにINKEY$では同時に複数のキーが押された場合に何が押されたのかを調べることは出来ません。同時にキーが押されたかどうかはキーポートの状態(押されたキーに電流が流れているかどうか)を調べれば簡単にかります。これは従来機種ではマシン語を使わないと調べることは不可能なのですがE500シリーズではPEEKとPOKEを使用することによりBASICでも調べることが可能なのです。
 まずは、FBh番地にA0hを書き込んで(デフォルトの値はE3h)割り込みを無くす必要があります。しかし、通常のキー入力はおろか[BREAK]すら無効になってしまう(元に戻すにはリセットを押すしかない)ので、割り込みの無効を行う前に

 KEY0,”POKE&FB,&E3”+CHR$13
を記述しておくのがよいでしょう。これを記述することにより、万一エラーが出ても自動的にキー入力が出来る状態(通常の状態)に戻ります。

 E500のキーは内部的には別表のように格子状に配列されています。まずは、この表の簡単な見方を説明します。

 例えば、[1]と[4]が同時に押されているかどうかを調べたい場合、 表の上の方をたどってみるとF0h番地、ビット5と書いてあると思います。これは、どういう意味かと言うと、F0h番地のビット5をONにするということです。何のことやら分からないという人のためにもう少し分かりやすく書きます。
 1バイトは8ビットであるというのは知ってますよね。例えば、16進数でABhという値は2進数では10101011と表記することができます。「1バイトは8ビットである」というのは言い換えるならば「1バイトは8桁の2進数で表記することができる」ということなのです。
 また、2進数というのは「1」か「0」、つまり、「ON」か「OFF」の2通りしかありません。ビット5をONにするというのは下から数えて6番目の位をONにする(0から始まっているので)ということなのです。だから、要は2の5乗(20h=32)を書き込めばよいのです。
だから、「1」が押されたかどうかを調べるのには、まず準備として、

 POKE &F0,&20

とすればよいのです。押されたかどうかはF2h番地をPEEKで読み取ることで分かります。

 A=PEEK &F2

 表の左を見たら分かりますが、[1]が押されていればF2h番地のビット6がONになるのです。つまり、Aに64(=40h)という値が代入されるのです。[4]が押された場合はF2h番地のビット5がONになり、Aに32(=20h)という値が代入されます。
 では、[1][4]同時に押した場合どうなるでしょうか。これは、ビット5、ビット6が両方ともONになるのでAに96(=60h)が代入されるのです。これを利用して[1][4]が押されているかどうかを調べるプログラムは以下の通りです。[1][4]を同時に押せばプログラムを終了します。

サンプルプログラム
10 KEY 0,"POKE&FB,&E3"+CHR$ 13
20 POKE &FB,&A0:POKE &F0,&20
30 CLS
40 A=PEEK &F2
50 IF A=&40 PRINT "1 ガ オサレタヨ":BEEP 1
60 IF A=&20 PRINT "4 ガ オサレタヨ":BEEP 1
70 IF A=&60 THEN 90
80 GOTO 30
90 PRINT "リョウホウ オサレマシタ"
100 POKE &FB,&E3

 では、[1][3]が押されたかどうかを調べるにはどうしたらよいでしょうか。[1]を調べるのにはF0h番地のビット5をONにしましたが、[3]を調べるのにはビット7をONにしなくてはならないのです。まあ、難しく考えずに、単純に2回に分けて判定を行えばよいだけですが(笑)。

 同時キー入力は慣れるまでは少し面倒ですが、今までオールBASICでは不可能と思われた同時キー入力のゲームを作ることが可能になります。これにより、2人対戦のゲームも作れるようになるのです。さらに、キー割り込みを無効化している分、処理の高速化も出来ます。ぜひ、どんどん使ってみてください。ただ、キー割り込みを無効化するとBREAKも使用できなくなるという問題があります。その場合ゲームを強制終了する場合、リセットボタンを押すしかありません。可能ならば特定のキーを押した場合にキー割り込みを許可(POKE&FB,&E3を実行)するような処置を取るのが望ましいです。

コラム 割り込み無効による高速化

 BASICプログラム実行時は様々な割り込み処理(何かあったらプログラム実行中でも別の処理に移る)が行われているのだ。
 キー割り込みはその中でも特に恩恵を受けていることであろう。というのも、プログラム実行中に[BREAK]キーにより処理が停止出来るのも割り込み処理のお陰なのである。簡単に言えば、BASICプログラム実行中は見えないところでIF文(?)による[BREAK]キーが押されたかどうかの判定しているようなものなのである。
 ただし、その判定が行われるのはキーポートに電流が流れたとき(つまり、何かキーが押されたとき)なので、常に判定が行われているものではない。だが、実際にゲームなどではキーを押している時間がかなり長く、自分の作ったプログラム以上の負荷が相当にかかっているのも事実である。何とこれにより全体の処理速度が15%程度低下してしまうのだ。これが、大きいか小さいかは人それぞれだろうが、ゲームではキー反応に多少なりとも影響してくるのは間違いではない。しかし、その半面、PEEKやPOKEなどの増加によるキー判定の負担が大きくなるので、実際の処理速度に関してはプログラムを組む人の腕に左右されるため一概に「確実速くなる」とは言えないのも事実である。
 だから、「同時キー入力が必要な場合のみキー割り込みを無効にする」と割り切って使うのも一つの手かもしれない。

《別表》

  F1h番地 F0h番地
F2h番地
  ビット2 ビット1 ビット0 ビット7 ビット6 ビット5 ビット4 ビット3 ビット2 ビット1 ビット0
ビット0 § CCE STO RCL
ビット1 2ndF FSE tan cos sin hyp
ビット2 PF5 1/x log In DEG HEX MENU
ビット3 PF4 X2 YX EXP
ビット4 PF3 DEL ÷ BASIC
ビット5 PF2 BS ×
ビット6 PF1 INS SPC CAP SHIFT
ビット7   +/- RET カナ CTRL
(注)「§」とは関数キーの中段の最も右にある「上下に矢印が付いたキー」のことです。
《参考文献》「PC−E500活用研究」(工学社刊)



同時キー入力サンプルゲーム

ドットレース


 ゲームを開始前にコントラストをかなり上げておくことをお薦めします。
また、このゲームをプレイ中はゲームボーイのように液晶部分が奥になるように本体を縦に持つとプレイしやすいでしょう。
 RUNをすると即スタートになります。そのとき、画面右端中央にあるドットがあなたです。[2][5]で左右に動かし、「8」で前進します。要はよくあるレースゲームと同じようなものです。障害物に当たらないように画面奥まで到達してください。完走出来た場合タイムが表示されます。タイムアタックをしてもおもしろいでしょう。なお、移動中は時間がゆっくり流れますので、静止した状態で左右に動かすよりも前進しながら左右に動かした方(ジグザグ走行)がよいタイムが出ます。ちなみに私のベストタイムは27秒17です。
 コースを変えたい場合は、10行の「A=RND−1」の「−1」を適当な数(ただし負の数)に変更してください。このゲームが難しすぎるという人は20行のFOR文の50を適当な数(20〜30くらい)に減らしてください。

《プログラムについて》

 同時キー入力の基本原理はすでに上記に書いていますので省略します。では、判定方法の方を見ていくことにしましょう。
 通常のレースゲームでよく見られるアクセルを押しながら左右にハンドルを切るというのと同じことをしているわけです。

ここで考えられる判定パターンは次の3通りです。

  (1) アクセルを押しているかどうか
  (2) 左にハンドルを切っているかどうか
  (3) 右にハンドルを切っているかどうか

(1)(2)の両方を満たしていれば左に進みながら加速するわけです。

(1)を満たしていればKに10hが代入される
(2)を満たしていればKに20hが代入される
(3)を満たしていればKに40hが代入される

 しかし、実は(1)と(2)の両方を満たしている場合、つまり、アクセルを押しながらハンドルを右に切るという場合はKの値は30hとなりますが、これをどのように判定するかが問題なのです。このままでは「アクセルを押しながらハンドルを切った場合」というさらに別の判定が必要(上記の3つの判定パターンにあてはまらないため)になります。

つまり、単純に考えた場合、以下の5通りの判定が必要なのです。

  1. ハンドルを右に切った場合
  2. ハンドルを左に切った場合
  3. アクセルを押しながらハンドルを右に切った場合
  4. アクセルを押しながらハンドルを左に切った場合
  5. アクセルのみを押した場合

 そこで登場するのが論理積ANDです。ANDはIF文で条件式の結合として使用されることが多いですが、本来の役目は論理積を行う演算子です。ここで「論理積とは何か」ということは省略(マニュアルを見てね)させてもらいますが、

  B=K AND &10

というように用いることによって好きなビットのみを取り出すことができ、

  IF (K AND &10)=&10 〜 

とすることにより、取り出したビットがONになっているかどうかを調べることができます。上記の例ではビット4がONになっているかどうかを調べています。こうすることにより、他のキーが押されたかどうかを考えることがなく判定が可能になります。

《プログラムリスト》

10 CLS :T=0:X=239:Y=15:A=RND -1
20 FOR I=1TO 50:GCURSOR (RND 220,RND 36):GPRINT "FFFFFFFFFFFF":NEXT
30 KEY 0,"POKE&FB,&E3"+CHR$ 13:BEEP 1:POKE &FB,&A0:POKE &F0,&40
40 T=T+0.1-(V+ABS W)/40:PRESET (X-V,Y-W):K=PEEK &F2
50 V=((K AND &10)=&10):W=((K AND &20)=&20)-((K AND &40)=&40)
60 P=POINT (X,Y):PSET (X,Y):X=X+V:Y=Y+W
70 IF P FOR I=0TO 20:LINE (X,Y)-(X+RND 18-9,Y+RND 18-9),X:BEEP 1,I*10,9:NEXT GOTO 90
80 IF X=0BEEP 1:LOCATE 9,1:PRINT USING "GOAL IN ! TIME="###.##";T ELSE 40
90 POKE &FB,&E3:WAIT :GPRINT :GPRINT :WAIT 0:GOTO 10
な〜んか、「ワンポイントテクニック」のわりに大きなページになっちゃいました(笑)


RETURN

inserted by FC2 system