Fix

通常、携帯ゲームでキーの状態を取得するためには、キーの状態をキーのリスナから変更させ、その変更させたデータに対して状態を問い合わせる。
MIDPの場合であれば、こんな感じだ。

//キーの状態
#define CKEY_0  0x00000001
#define CKEY_1  0x00000002
#define CKEY_2  0x00000004
#define CKEY_3  0x00000008
#define CKEY_4  0x00000010
...

private int key;

public void keyPressed(int keyCode){
    int i=0;
    switch(keyCode){
    case Canvas.KEY_NUM0: i |= CKEY_0; break;
    case Canvas.KEY_NUM1: i |= CKEY_1; break;
    case Canvas.KEY_NUM2: i |= CKEY_2; break;
    case Canvas.KEY_NUM3: i |= CKEY_3; break;
    case Canvas.KEY_NUM4: i |= CKEY_4; break;
    ...
    }
    key |= i;
}

public void keyReleased(int keyCode){
    ...
}

public boolean isKey(int keyCode){
    return ((key & keyCode) != 0);
}

このisKeyによってキーの状態を問い合わせる。
しかし、この書き方だと、以下のコードで例外が発生する場合があるのだ。

int n=0;
if(isKey(CKEY_0)){
    n++;
}
if(isKey(CKEY_0)){
    if(n == 0) throw new RuntimeException();
}

keyPressedは、別スレッドから実行されているので、最初のキー判定を抜けた瞬間にキーの状態が書き換えられた場合、自分の思った動作はしない。
もしこのまま書くとすれば、キーの判定は1度しかやってはいけないことになる。
それ以降は書き換わってしまう可能性があるのだから。
しかし、その為に設計を変更するのはめんどうなのだ。
なんとかして、1フレームの間にキーを何回取得しても、正常に動作するようにしたい。
そんなときに使うのが、FixKey。
1フレームの間はキーの状態が変更されないようにするのだ。

private int key;
private int key2;

public void keyPressed(int keyCode){
    int i=0;
    switch(keyCode){
    case Canvas.KEY_NUM0: i |= CKEY_0; break;
    case Canvas.KEY_NUM1: i |= CKEY_1; break;
    ...
    }
    key2 |= i;
}

public void keyUpdate(){
    key = key2;
}

内部ではリアルタイムに情報を更新しているが、自分が状態を問い合わせるのは、keyUpdate()ごとに更新される状態のみである。
なので、keyUpdate()を1フレームごとに呼び出せば、そのフレームの間は、キーの状態が変更しないのだ。
つまり、

keyUpdate();

int n=0;
if(isKey(CKEY_0)){
    n++;
}
if(isKey(CKEY_0)){
    if(n == 0) throw new RuntimeException();
}

このコードで例外が発生することはない。


というか、うちの会社の人たちが共通で使ってるライブラリってFixKeyになってないんですよ。
しかも、最初はそれに気づかなかったので、上記のようなバグを出しちゃいましたよ(;´Д`)
かなり昔からある(らしい)ライブラリなのに、なんで今までそのことが指摘されなかったのかが不思議でならないです(;´Д`)