A08.ユーザーイベント
ユーザーが独自に設定したイベントを発行/受信します。
まずは、独自のボタンコントロールの処理を行います。
操作方法
X軸回転 :X軸方向への回転をON/OFFします。
Y軸回転 :Y軸方向への回転をON/OFFします。
Z軸回転 :Z軸方向への回転をON/OFFします。
速度変更:回転速度を0.01〜0.1で変化させます。1回押すと+0.01です。
拡張項目:無効/有効、非表示/表示を切り替えます。
情報表示:現在は使用できません。
終了確認:現在は使用できません。
終了 :プログラムを終了します。
設定項目に合わせて、画面右の立方体が動きます。

タイプとIDを定義
ユーザーイベントを使用するにあたって、わかりやすいタイプ名とIDを定義しておきます。
ユーザーIDにはここで定義したID以外も使用することが可能です。
ここで定義したものは予約語みたいなものです。
//ユーザーイベントタイプ
enum EUSER_MESSAGE_TYPE
{
EUMT_BUTTON_CLICKED = 0
};
//ユーザーイベントID
enum USER_ID
{
UID_NONE = 0,
UID_OK,
UID_CANCEL,
UID_ABORT,
UID_RETRY,
UID_IGNORE,
UID_YES,
UID_NO,
UID_CLOSE,
UID_HELP,
UID_EXIT
};
|
ユーザーイベント送信
postEventFromUserを使用してユーザーイベントを送信します。
ユーザーデータには明確な使用方法が存在していないので、以下のようにします。
UserData1:イベントタイプ(クリックされた、ドラッグされたなど)
UserData2:イベントID
2つの値を設定して送信します。
//ユーザーイベント送信
void postEvent(s32 eventType, s32 eventId)
{
SEvent event;
event.EventType = EET_USER_EVENT;
event.UserEvent.UserData1 = eventType;
event.UserEvent.UserData2 = eventId;
device->postEventFromUser(event);
}
|

postEventFromUser
ユーザーが作成したイベントをエンジンに送信します。
void postEventFromUser
(
SEvent event
)
|
event イベント内容を設定します。
ユーザー用に用意されているデータは、以下のようになっています。
event.UserEvent.UserData1 int型のデータを設定します。
event.UserEvent.UserData2 int型のデータを設定します。
event.UserEvent.UserData3 float型のデータを設定します。
|
イベント処理
イベントレシーバーにユーザーイベントが送信されてくるので、
ここでイベントをそれぞれ処理します。
UID_EXITが設定されていた場合は、デバイスを終了させます。
他のIDが指定されていた場合は、対応する処理を行います。
ユーザーイベント自体は以上で終了ですが、呼び出し元がないので、
次から呼び出しもとの設定も行います。
//レシーバー設定
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
//ユーザーイベント処理
if(event.EventType == EET_USER_EVENT)
{
//イベントが起こったID
s32 id = event.UserEvent.UserData2;
switch(event.UserEvent.UserData1)
{
//ボタンが押された時の処理
case EUMT_BUTTON_CLICKED:
switch(id)
{
case 0x10:
b_state.rx = 1-b_state.rx;
break;
case 0x11:
b_state.ry = 1-b_state.ry;
break;
case 0x12:
b_state.rz = 1-b_state.rz;
break;
case 0x13:
b_state.speed += 0.01f;
if(b_state.speed > 0.1f)b_state.speed = 0.01f;
break;
case 0x14:
if(b_state.bShow)b_state.bShow = false;
else b_state.bShow = true;
break;
case UID_EXIT:
//デバイスを終了させます
device->closeDevice();
break;
default:
return true;
}
return true;
break;
default:
return true;
}
}
return false;
}
};
|
データ用意
マウスで処理していくので、マウス/ボタン用のデータを作成していきます。
move:マウスの現在の座標が入ります。
press:マウスの左ボタンを押した座標が入ります。
release:マウスの左ボタンを離した座標が入ります。
ボタン用のデータとして、XYZに対しての回転許可、回転速度、表示のON/OFFを保存できるようにします。
//マウスデータ
typedef struct
{
position2d move;
s32 state;
position2d press; //state = 1
position2d release; //state = 0
}mouseData;
mouseData mouse;
//ボタン設定
typedef struct
{
s32 rx,ry,rz;
f32 speed;
bool bShow;
}buttonState;
buttonState b_state = {0,0,0,0.01f,false};
|
マウス用関数
マウスの情報を消去、マウスの座標チェック、状態取得を行っています。
状態取得関数では、マウスがクリックされた情報をもとに、イベントを送信しています。
もっと綺麗なやい方がありそうですが…。
//マウス情報消去
void mouseClear()
{
mouse.state = 0;
mouse.press.X = -1;
mouse.press.Y = -1;
mouse.release.X = -1;
mouse.release.Y = -1;
}
//指定範囲内にマウスがあるか
bool checkRange(s32 mouseX, s32 mouseY, s32 x, s32 y, s32 w, s32 h)
{
if( x <= mouseX && mouseX <= x+w &&
y <= mouseY && mouseY <= y+h)
return true;
return false;
}
//マウスの状態取得
int getState(mouseData m_data, s32 ctrlID, s32 x, s32 y, s32 w, s32 h)
{
s32 state = 1;
//マウスがコントロール内か
if(checkRange(m_data.move.X, m_data.move.Y, x,y,w,h))
{
//左ボタンが押されているか
if(m_data.state == 1)
{
//押した場所がコントロール内か
if(checkRange(m_data.press.X, m_data.press.Y, x,y,w,h))
state = 3;
}
//押されていない
else
{
//押して離した場所が同一のコントロールの場合
if( checkRange(m_data.press.X, m_data.press.Y, x,y,w,h) &&
checkRange(m_data.release.X, m_data.release.Y, x,y,w,h))
{
mouseClear();
postEvent(EUMT_BUTTON_CLICKED, ctrlID);
}
state = 2;
}
}
return state;
}
|
コントロール
コントロールを作成します。
・コントロールの基本となるベースコントロール。
・今回ユーザーイベントで使用するボタンコントロール。
・コントロールを処理するGUI。
ベースコントロールは、今後色々なコントロールを追加するかもしれないので、
基本的な処理をするように設定しています。
ボタンコントロールは、追加、キャプション変更、表示の機能を持っています。
マウスの状態によって、ボタンの色が変わるようになっています。
GUIは、各機能の呼び出しと、全ての描画を行います。
メッセージウインドウなどの追加により、アクティブウインドウが変更された場合などに
各コントロールを調整したりするようにします。
//ベースコントロールクラス
class BaseCtrl
{
public:
//コントロール初期化
void initialize(c8* name)
{
ctrlName = name;
bVisible = true;
bEnable = true;
}
//コントロール名取得
const c8* GetCtrlName() const {return ctrlName;}
//コントロールの可視状態設定
void setVisible(bool bState){bVisible = bState;}
//コントロールの可視状態取得
bool getVisible(){return bVisible;}
//コントロールの有効無効設定
void setEnable(bool bState){bEnable = bState;}
//コントロールの有効無効取得
bool getEnable(){return bEnable;}
private:
c8* ctrlName;
bool bVisible;
bool bEnable;
};
//ボタンコントロールクラス
class ButtonCtrl : public BaseCtrl
{
public:
ButtonCtrl(){init();}
~ButtonCtrl(){}
//コントロール初期化
void init()
{
name = NULL;
cx = cy = cw = ch = 0;
state = 0;
ctrlID = 0;
bCenter = false;
initialize("ButtonCtrl");
}
//コントロール追加
bool add(c8* caption, s32 id, s32 x, s32 y, s32 w, s32 h, bool center)
{
name = caption;
cx = x;
cy = y;
cw = w;
ch = h;
ctrlID = id;
bCenter = center;
return true;
}
//キャプション変更
void setText(c8*caption)
{
name = caption;
}
//コントロール表示
void draw(mouseData m_data, bool bActive)
{
s32 len;
s32 state = 0;
s32 sx = 4; //左寄せ
s32 sy = (ch - 16) / 2; //中央寄せ
//カラー 無効 通常 マウスオン クリック
SColor back[4] = {0xFF999999, 0xFF00FF00, 0xFF33FF33, 0xFF66FF66};
SColor top[4] = {0xFF333333, 0xFF006600, 0xFF339933, 0xFF66CC66};
SColor font[4] = {0xFFCCCCCC, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
//名前チェック
if(name == NULL || strlen(name) == 0)
return;
len = strlen(name);
//中央寄せ
if(bCenter)
sx = (cw - len*8)/2; //中央寄せ
//表示可の場合は表示する
if(getVisible())
{
//有効かどうか
if(getEnable())
{
//アクティブかどうか
if(bActive)
{
//詳細を取得する
state = getState(m_data, ctrlID, cx,cy,cw,ch);
}
else state = 1;
}
//ボタン作成
FillRect(driver, cx,cy,cw,ch, back[state]);
FillRect(driver, cx+1,cy+1,cw-2,ch-2, top[state]);
Printf(driver,cx+sx,cy+sy,font[state],name);
}
}
private:
c8* name;
s32 cx,cy,cw,ch;
s32 state;
s32 ctrlID;
bool bCenter;
};
class UserGUI
{
public:
UserGUI(){init();}
~UserGUI(){}
void init()
{
btnCnt = 0;
}
void setEnable(s32 num, bool state)
{
btn[num].setEnable(state);
}
void setVisible(s32 num, bool state)
{
btn[num].setVisible(state);
}
void setText(s32 num, c8* caption)
{
btn[num].setText(caption);
}
bool button(c8* caption, s32 id, s32 x, s32 y, s32 w, s32 h, bool center)
{
if(btnCnt > 9)
return false;
btn[btnCnt].add(caption, id,x,y,w,h,center);
btnCnt++;
return true;
}
void drawAll()
{
u32 i;
for(i=0;i<btnCnt;i++)
btn[i].draw(mouse, true);
}
private:
s32 btnCnt;
ButtonCtrl btn[10];
};
|
イベントレシーバー調整
マウスでの処理を追加します。以下を追加しています。
・左ボタンを押した座標を取得。
・左ボタンを離した座標を取得。
・マウスの座標を取得。
virtual bool OnEvent(const SEvent& event)
{
//マウスイベント
if(event.EventType == EET_MOUSE_INPUT_EVENT)
{
switch(event.MouseInput.Event)
{
case EMIE_LMOUSE_PRESSED_DOWN:
mouse.state = 1;
mouse.press.X = event.MouseInput.X;
mouse.press.Y = event.MouseInput.Y;
return true;
case EMIE_LMOUSE_LEFT_UP:
mouse.state = 0;
mouse.release.X = event.MouseInput.X;
mouse.release.Y = event.MouseInput.Y;
return true;
case EMIE_MOUSE_MOVED:
mouse.move.X = event.MouseInput.X;
mouse.move.Y = event.MouseInput.Y;
return true;
default:
return true;
}
}
//ユーザーイベント処理
if(event.EventType == EET_USER_EVENT)
|
ボタン追加
画面左側にボタンを8個追加します。
newで作成しているので、最後に忘れずにdeleteしてください。
//ボタン作成
c8 buf[32];
UserGUI *userGui = new UserGUI();
userGui->button("X軸回転", 0x10, 16, 16,144,20, true);
userGui->button("Y軸回転", 0x11, 16, 40,144,20, true);
userGui->button("Z軸回転", 0x12, 16, 64,144,20, true);
userGui->button("速度変更", 0x13, 16, 88,144,20, true);
userGui->button("拡張項目", 0x14, 16,112,144,20, true);
userGui->button("情報表示", 0x15, 16,136,144,20, true);
userGui->button("終了確認", 0x16, 16,160,144,20, true);
userGui->button("終了", UID_EXIT, 16,184,144,20, true);
|
表示
ボタンには、以下の特別な処理を行っています。
・回転速度によってボタンのキャプションを変更しています。
・表示状態によって、ボタンを無効化/有効化、表示/非表示を切り替えています。
無効のボタンは表示されていても、押すことができません。
非表示のボタンは、画面に表示もされません。
smgr->drawAll()の後に、GUIの描画を行っています。
これは、一番前面に表示するためです。もしsmgr->drawAll()の前に描画したら、
ポリゴンなどの後ろに描画されるようになります。
while(device->run())
{
driver->beginScene(true,true,0xFF6060FF);
//背景作成
FillRect(driver, 164,16,144,160, 0xFFFFFFFF);
FillRect2(driver,165,17,142,158, 0xFF666666,
0xFF000000,0xFF000000,0xFF666666,PS_SPECIAL);
//ボタン設定
sprintf(buf, "速度変更:%0.2f", b_state.speed);
userGui->setText(3, buf); //キャプション変更
userGui->setEnable(5, b_state.bShow); //有効/無効設定
userGui->setVisible(6, b_state.bShow); //表示/非表示設定
//図形作成
node->setRotation(vector3df(rx,ry,rz));
if(b_state.rx)rx += b_state.speed;
if(b_state.ry)ry += b_state.speed;
if(b_state.rz)rz += b_state.speed;
//シーンの描画
smgr->drawAll();
//GUI描画
userGui->drawAll();
driver->endScene();
}
driver->drop();
delete userGui;
|

図形/文字の描画
図形/文字の描画には、今までに作成してきたDraw系の関数を使用しています。
darw.cppとdraw.hにまとめてあるのをインクルードしています。
今回作成したボタンはFillRectで作成してありますが、DrawImageを使用すれば
画像に差し替えることも可能です。
また、「ボタンが押された」というのをわかりやすくするために
表示座標を変更してみるのもよいかもしれません。
|
ダウンロード
今回作成したファイル一式です。
cube.xに法線を加えたものも同梱してあります。
宿題
1.ボタンの背景に画像を使用してみましょう。
2.クリック状態により、画像が変更するようにしてみましょう。
3.クリック感を出すために、クリックしたボタンの座標を1ずらしてみましょう。
|