Inputクラスの実装。
の第2版。旧版はこちら現状対応しているのは、大きさのあるボタンだけ。=つまりタッチスクリーン。
ゲーム側からの使い方=================
○ボタンの追加・削除
bool addButton(id_t, rect_t);
bool moveButton(id_t, rect_t);
bool removeButton(id_t);
bool hasButton(id_t);
追加・削除は状況に応じてどんどん行う。
画面が変わったときに一斉にadd,removeを行い、タッチスクリーン上のボタンが動いているときにmoveを行うイメージ。
ボタンを一意に表すidは、ゲーム側が自由に設定できる。そのほうがゲーム側が便利なので。
その代わり、idがかぶらないようにする責任もゲーム側が負う。(ゲーム側はたぶんenumでボタンを管理するだろうが、enumのスタート値にオフセットを付けておくとか)
idがかぶると、ボタン追加に失敗する。
なお、id=0は予約済。エラー扱い。id=1も予約するかもしれない。
とりあえず、idは0x1000_0000以上の値を使うようにしたい。
○ボタンの有効・無効
bool enableButton(id_t);
bool disableButton(id_t);
bool isEnableButton(id_t);
追加したらデフォルトで有効になる。
無効にすると、ボタンの中の時間は止まる。つまり、prevOnとかが維持される。
○ボタン状態取得
bool isOn(id_t);
bool isOff(id_t);
bool hasPressed(id_t);
bool hasReleased(id_t);
bool hasMoved(id_t);
bool hasRepeated();uint32_t getPressedCount();point_t getPoint(id_t);
point_t getMovedSize(id_t);
point_t getPrevPoint(id_t);
SButtonSized& getButton(id_t);
一般的な情報がそのまま取れる。
On,Offの状態、Press,Releaseの瞬間が取得可能。大きさのあるボタンなので、ボタンのどこを押しているかも取得可能。
getPoint()でボタン内のどこを押したか取得。
また、hasMoved(), getMovedSize()でボタン内で押下場所が動いたことも検出可。
リピート押しはまだ実装していない。リピート押しを実装。
hasRepeated()で取得可能。
リピート間隔は
void setRepeatCount(size_t c);
で指定する。取得も可能。
size_t getRepeatCount();
デフォルト値は0。つまりリピート判定をしない。その場合、hasPressed()の瞬間だけhasRepeated()が真になる。
ボタンごとにリピート間隔を指定できるように作ってはあるが、あまりリピート間隔がバラバラすぎるとUIとして良くないので、推奨値のようなものも用意しておいた。メニュー周りなどは同じ値を使うのが望ましいだろう。
連打回数を取得可能。
getPressedCount()で取得できる。iOSの機能にあったので付け足した。主にダブルタップ用か。getButton()で取得してからisOn()などを使うこともできる。
存在しないidを指定したときの動作は未定義。
○サンプリング
void sampling();
ボタン状態をサンプリング(更新)する。
ゲームのexecが走る前に呼び出すのがよい。
○デバッグ表示
void description();
登録されているボタンすべての情報が表示される。
Inputクラス内部の実装=======================
○ボタンの持ち方
マップ。ボタンの追加・削除などの操作は、ほぼmapの操作だけ。
SInputButton構造体は、Inputクラスの中でのボタンの状態を表す。
std::map<id_t, SInputButton>
struct Input::SInputButton{
id_t id; //id. mapのキーと同じ値
bool isEnable; //有効かどうか
SButtonSized button; //ボタン
char name[16]; //識別名
};
SButtonSized構造体は、大きさのあるボタンを表す。
SButton構造体は、大きさの無いボタンを表す。(Aボタンや、キーボードのキーなど想定)
struct SButton{
bool isOn();
bool isOff();
bool hasPressed();
bool hasReleased();
bool hasRepeated();
uint32_t getPressedCount();
void update(bool isOn);
private:
typedef enum State {
INVALID = -1,
OFF = 0,
ON
} State;
State on_;
State prevOn_;
};
struct SButtonSized: public SButton{
bool hasMoved();
point_t getPoint();
point_t getPrevPoint();
point_t getMovedSize();
rect_t getRect();
void setRect(rect_t);
void update(bool isOn, point_t pos/*=point_t(0.f,0.f)*/);
private:
rect_t rect;
point_t point;
point_t prevPoint;
};
○ボタンの状態
ONとOFFだけでなく、INVALIDも追加し3値で扱うようにした。
ボタンが追加されたとき(addButton)または有効になったとき(enableButton)は、OFFでなくINVALIDの状態から始まるようにする。
hasPressed()などは、前状態がOFFの場合にのみフラグが立つようにする。
でないと、ボタンが生成される前からボタンの位置をタッチしていた場合、ボタンが生成された瞬間にhasPressed()になってしまう。
これは、ボタンを押したことによって画面遷移した瞬間、同じ位置にまた別のボタンがあったような場合、その瞬間にまたボタンを押したことになってしまうから。
また、アプリの処理が中断したときにINVALID状態に遷移することで、iOSでいうtouchCancelledへの対応も可能になる。
○update()
現状態を前状態にコピーし、新状態を代入。
ボタンを押している時間をカウント(hasRepeated()のため)
SButtonとSButtonSizedのupdate()は、オーバーライドしているわけではない
○サンプリングの方法
iPhoneのタッチイベントを保持しておき、サンプリング周期ごとに翻訳。
InputSamplerクラスが行う。
○流れ
(iPhoneコード)
→-(void) touchesBegan: withEvent;
→InputSampler::addTouchBegan();
(ゲーム内)
→Input::sampling();
→InputSampler::samplingData(Input&);
○コード
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (id touch in touches) {
inputSampler.addTouchBegan(touch);
}
}
void InputSampler::addTouchBegan(UITouch *touch){
//リストに追加
}
void InputSampler::samplingData(Input&){
//Inputの内部を直接触る
//on->prevOn, point->prevPointに代入
//リストを走査、ボタンの範囲内のタッチがあったら、on,pointを書き込み
}
PR