Tutorial 24 - CursorControl - Chip - 24-08-2015
Tutorial 24: CursorControl
Mostriamo come modificare il cursore del mouse e offriamo alcune utili funzioni per crearne di nuovi.
Possiamo anche usare l'esempio per fare sperimentare la manipolazione de mouse in generale.
Codice PHP: #include <irrlicht.h> #include "driverChoice.h"
using namespace irr; using namespace core; using namespace scene; using namespace video; using namespace io; using namespace gui;
#ifdef _IRR_WINDOWS_ #pragma comment(lib, "Irrlicht.lib") #endif
const int DELAY_TIME = 3000;
enum ETimerAction { ETA_MOUSE_VISIBLE, ETA_MOUSE_INVISIBLE, };
La struttura permette l'esecuzione ritardata di alcune azioni.
Codice PHP: struct TimerAction { u32 TargetTime; ETimerAction Action; }; struct SAppContext { SAppContext() : Device(0), InfoStatic(0), EventBox(0), CursorBox(0), SpriteBox(0) , ButtonSetVisible(0), ButtonSetInvisible(0), ButtonSimulateBadFps(0) , ButtonChangeIcon(0) , SimulateBadFps(false) { }
void update() { if (!Device) return; u32 timeNow = Device->getTimer()->getTime(); for ( u32 i=0; i < TimerActions.size(); ++i ) { if ( timeNow >= TimerActions[i].TargetTime ) { runTimerAction(TimerActions[i]); TimerActions.erase(i); } else { ++i; } } }
void runTimerAction(const TimerAction& action) { if (ETA_MOUSE_VISIBLE == action.Action) { Device->getCursorControl()->setVisible(true); ButtonSetVisible->setEnabled(true); } else if ( ETA_MOUSE_INVISIBLE == action.Action) { Device->getCursorControl()->setVisible(false); ButtonSetInvisible->setEnabled(true); } }
Aggiungiamo un altra icona che l'utente può cliccare e selezionare come cursore futuro.
Codice PHP: void addIcon(const stringw& name, const SCursorSprite &sprite, bool addCursor=true) { // Sprites are just icons - not yet cursors. They can be displayed by // Irrlicht sprite functions and be used to create cursors. SpriteBox->addItem(name.c_str(), sprite.SpriteId); Sprites.push_back(sprite);
// create the cursor together with the icon? if ( addCursor ) {
Qui creiamo un cursore da uno sprite
Codice PHP: Device->getCursorControl()->addIcon(sprite);
// ... e lo aggiungo alla listbox per la scelta dei cursori. CursorBox->addItem(name.c_str()); } }
IrrlichtDevice * Device; gui::IGUIStaticText * InfoStatic; gui::IGUIListBox * EventBox; gui::IGUIListBox * CursorBox; gui::IGUIListBox * SpriteBox; gui::IGUIButton * ButtonSetVisible; gui::IGUIButton * ButtonSetInvisible; gui::IGUIButton * ButtonSimulateBadFps; gui::IGUIButton * ButtonChangeIcon; array<TimerAction> TimerActions; bool SimulateBadFps; array<SCursorSprite> Sprites; };
Funzione di aiuto, serve a mostrare gli eventi passandoli in una stringw che vedremo
Codice PHP: void PrintMouseEventName(const SEvent& event, stringw &result) { switch ( event.MouseInput.Event ) { case EMIE_LMOUSE_PRESSED_DOWN: result += stringw(L"EMIE_LMOUSE_PRESSED_DOWN"); break; case EMIE_RMOUSE_PRESSED_DOWN: result += stringw(L"EMIE_RMOUSE_PRESSED_DOWN"); break; case EMIE_MMOUSE_PRESSED_DOWN: result += stringw(L"EMIE_MMOUSE_PRESSED_DOWN"); break; case EMIE_LMOUSE_LEFT_UP: result += stringw(L"EMIE_LMOUSE_LEFT_UP"); break; case EMIE_RMOUSE_LEFT_UP: result += stringw(L"EMIE_RMOUSE_LEFT_UP"); break; case EMIE_MMOUSE_LEFT_UP: result += stringw(L"EMIE_MMOUSE_LEFT_UP"); break; case EMIE_MOUSE_MOVED: result += stringw(L"EMIE_MOUSE_MOVED"); break; case EMIE_MOUSE_WHEEL: result += stringw(L"EMIE_MOUSE_WHEEL"); break; case EMIE_LMOUSE_DOUBLE_CLICK: result += stringw(L"EMIE_LMOUSE_DOUBLE_CLICK"); break; case EMIE_RMOUSE_DOUBLE_CLICK: result += stringw(L"EMIE_RMOUSE_DOUBLE_CLICK"); break; case EMIE_MMOUSE_DOUBLE_CLICK: result += stringw(L"EMIE_MMOUSE_DOUBLE_CLICK"); break; case EMIE_LMOUSE_TRIPLE_CLICK: result += stringw(L"EMIE_LMOUSE_TRIPLE_CLICK"); break; case EMIE_RMOUSE_TRIPLE_CLICK: result += stringw(L"EMIE_RMOUSE_TRIPLE_CLICK"); break; case EMIE_MMOUSE_TRIPLE_CLICK: result += stringw(L"EMIE_MMOUSE_TRIPLE_CLICK"); break; default: break; } }
Funzione di aiuto, per mostrare tutti gli stati del mouse in una stringw che vedremo
Codice PHP: void PrintMouseState(const SEvent& event, stringw &result) { result += stringw(L"X: "); result += stringw(event.MouseInput.X); result += stringw(L"\n");
result += stringw(L"Y: "); result += stringw(event.MouseInput.Y); result += stringw(L"\n");
result += stringw(L"Wheel: "); result += stringw(event.MouseInput.Wheel); result += stringw(L"\n");
result += stringw(L"Shift: "); if ( event.MouseInput.Shift ) result += stringw(L"true\n"); else result += stringw(L"false\n");
result += stringw(L"Control: "); if ( event.MouseInput.Control ) result += stringw(L"true\n"); else result += stringw(L"false\n");
result += stringw(L"ButtonStates: "); result += stringw(event.MouseInput.ButtonStates); result += stringw(L"\n");
result += stringw(L"isLeftPressed: "); if ( event.MouseInput.isLeftPressed() ) result += stringw(L"true\n"); else result += stringw(L"false\n");
result += stringw(L"isRightPressed: "); if ( event.MouseInput.isRightPressed() ) result += stringw(L"true\n"); else result += stringw(L"false\n");
result += stringw(L"isMiddlePressed: "); if ( event.MouseInput.isMiddlePressed() ) result += stringw(L"true\n"); else result += stringw(L"false\n");
result += stringw(L"Event: ");
PrintMouseEventName(event, result);
result += stringw(L"\n"); }
Il tipico event receiver.
Codice PHP: class MyEventReceiver : public IEventReceiver { public: MyEventReceiver(SAppContext & context) : Context(context) { }
virtual bool OnEvent(const SEvent& event) { if (event.EventType == EET_GUI_EVENT ) { switch ( event.GUIEvent.EventType ) { case EGET_BUTTON_CLICKED: { u32 timeNow = Context.Device->getTimer()->getTime(); TimerAction action; action.TargetTime = timeNow + DELAY_TIME; if ( event.GUIEvent.Caller == Context.ButtonSetVisible ) { action.Action = ETA_MOUSE_VISIBLE; Context.TimerActions.push_back(action); Context.ButtonSetVisible->setEnabled(false); } else if ( event.GUIEvent.Caller == Context.ButtonSetInvisible ) { action.Action = ETA_MOUSE_INVISIBLE; Context.TimerActions.push_back(action); Context.ButtonSetInvisible->setEnabled(false); } else if ( event.GUIEvent.Caller == Context.ButtonSimulateBadFps ) { Context.SimulateBadFps = Context.ButtonSimulateBadFps->isPressed(); } else if ( event.GUIEvent.Caller == Context.ButtonChangeIcon ) {
Sostituisco l'icona del cursore esistente con un'altra icona. L'utente deve selezionarli entrambe, l'icona da cambiare e quella nuova.
Codice PHP: s32 selectedCursor = Context.CursorBox->getSelected(); s32 selectedSprite = Context.SpriteBox->getSelected(); if ( selectedCursor >= 0 && selectedSprite >= 0 ) {
Qui sostituiamo le icone.
Codice PHP: Context.Device->getCursorControl()->changeIcon((ECURSOR_ICON)selectedCursor, Context.Sprites[selectedSprite] );
E mostriamo l'icona.
Codice PHP: Context.Device->getCursorControl()->setActiveIcon( ECURSOR_ICON(selectedCursor) ); } } } break; case EGET_LISTBOX_CHANGED: case EGET_LISTBOX_SELECTED_AGAIN: { if ( event.GUIEvent.Caller == Context.CursorBox ) {
Mostriamo quale cursore ha scelto l'utente
Codice PHP: s32 selected = Context.CursorBox->getSelected(); if ( selected >= 0 ) {
Qui impostiamo il nuovo cursore per essere usato nella nostra finestra.
Codice PHP: Context.Device->getCursorControl()->setActiveIcon( ECURSOR_ICON(selected) ); } } } break; default: break; } }
if (event.EventType == EET_MOUSE_INPUT_EVENT) { stringw infoText; PrintMouseState(event, infoText); Context.InfoStatic->setText(infoText.c_str()); if ( event.MouseInput.Event != EMIE_MOUSE_MOVED && event.MouseInput.Event != EMIE_MOUSE_WHEEL ) // no spam { infoText = L""; PrintMouseEventName(event, infoText); Context.EventBox->insertItem(0, infoText.c_str(), -1); } }
return false; }
private: SAppContext & Context; };
Usiamo diversi files con immagini come frame per le animazioni nelle icone come cursori.
Le immagini in questi file devono essere tutte della stessa dimensione.
In caso di successo ritorniamo l'indice dello sprite Return sprite altrimenti -1
Codice PHP: s32 AddAnimatedIconToSpriteBank( gui::IGUISpriteBank * spriteBank, video::IVideoDriver* driver, const io::path& file, const array< rect<s32> >& rects, u32 frameTime ) { if ( !spriteBank || !driver || !rects.size() ) return -1;
video::ITexture * tex = driver->getTexture( file ); if ( tex ) { array< rect<s32> >& spritePositions = spriteBank->getPositions(); u32 idxRect = spritePositions.size(); u32 idxTex = spriteBank->getTextureCount(); spriteBank->addTexture( tex );
SGUISprite sprite; sprite.frameTime = frameTime;
array< SGUISprite >& sprites = spriteBank->getSprites(); for ( u32 i=0; i < rects.size(); ++i ) { spritePositions.push_back( rects[i] );
gui::SGUISpriteFrame frame; frame.rectNumber = idxRect+i; frame.textureNumber = idxTex; sprite.Frames.push_back( frame ); }
sprites.push_back( sprite ); return sprites.size()-1; }
return -1; }
Creiamo una icone non animata dalla posizione e dal file forniti e lo mettiamo nell'archivio degli sprite.
Potremo usare queste icone come cursori in seguito.
Codice PHP: s32 AddIconToSpriteBank( gui::IGUISpriteBank * spriteBank, video::IVideoDriver* driver, const io::path& file, const core::rect<s32>& rect ) { if ( !spriteBank || !driver ) return -1;
video::ITexture * tex = driver->getTexture( file ); if ( tex ) { core::array< core::rect<irr::s32> >& spritePositions = spriteBank->getPositions(); spritePositions.push_back( rect ); array< SGUISprite >& sprites = spriteBank->getSprites(); spriteBank->addTexture( tex );
gui::SGUISpriteFrame frame; frame.rectNumber = spritePositions.size()-1; frame.textureNumber = spriteBank->getTextureCount()-1;
SGUISprite sprite; sprite.frameTime = 0; sprite.Frames.push_back( frame );
sprites.push_back( sprite );
return sprites.size()-1; }
return -1; }
int main() { video::E_DRIVER_TYPE driverType = driverChoiceConsole(); if (driverType==video::EDT_COUNT) return 1;
IrrlichtDevice * device = createDevice(driverType, dimension2d<u32>(640, 480)); if (device == 0) return 1; // could not create selected driver.
// It's sometimes of interest to know how the mouse behaves after a resize device->setResizable(true);
device->setWindowCaption(L"Cursor control - Irrlicht engine tutorial"); video::IVideoDriver* driver = device->getVideoDriver(); IGUIEnvironment* env = device->getGUIEnvironment();
gui::IGUISpriteBank * SpriteBankIcons;
SAppContext context; context.Device = device;
rect< s32 > rectInfoStatic(10,10, 200, 200); env->addStaticText (L"Cursor state information", rectInfoStatic, true, true); rectInfoStatic.UpperLeftCorner += dimension2di(0, 15); context.InfoStatic = env->addStaticText (L"", rectInfoStatic, true, true); rect< s32 > rectEventBox(10,210, 200, 400); env->addStaticText (L"click events (new on top)", rectEventBox, true, true); rectEventBox.UpperLeftCorner += dimension2di(0, 15); context.EventBox = env->addListBox(rectEventBox); rect< s32 > rectCursorBox(210,10, 400, 250); env->addStaticText (L"cursors, click to set the active one", rectCursorBox, true, true); rectCursorBox.UpperLeftCorner += dimension2di(0, 15); context.CursorBox = env->addListBox(rectCursorBox); rect< s32 > rectSpriteBox(210,260, 400, 400); env->addStaticText (L"sprites", rectSpriteBox, true, true); rectSpriteBox.UpperLeftCorner += dimension2di(0, 15); context.SpriteBox = env->addListBox(rectSpriteBox);
context.ButtonSetVisible = env->addButton( rect<s32>( 410, 20, 560, 40 ), 0, -1, L"set visible (delayed)" ); context.ButtonSetInvisible = env->addButton( rect<s32>( 410, 50, 560, 70 ), 0, -1, L"set invisible (delayed)" ); context.ButtonSimulateBadFps = env->addButton( rect<s32>( 410, 80, 560, 100 ), 0, -1, L"simulate bad FPS" ); context.ButtonSimulateBadFps->setIsPushButton(true); context.ButtonChangeIcon = env->addButton( rect<s32>( 410, 140, 560, 160 ), 0, -1, L"replace cursor icon\n(cursor+sprite must be selected)" );
// set the names for all the system cursors for ( int i=0; i < (int)gui::ECI_COUNT; ++i ) { context.CursorBox->addItem(stringw( GUICursorIconNames[i] ).c_str()); }
Create sprites which then can be used as cursor icons.
Codice PHP: SpriteBankIcons = env->addEmptySpriteBank(io::path("cursor_icons")); context.SpriteBox->setSpriteBank(SpriteBankIcons);
// create one animated icon from several files array< io::path > files; files.push_back( io::path("../../media/icon_crosshairs16x16bw1.png") ); files.push_back( io::path("../../media/icon_crosshairs16x16bw2.png") ); files.push_back( io::path("../../media/icon_crosshairs16x16bw3.png") ); files.push_back( io::path("../../media/icon_crosshairs16x16bw3.png") ); files.push_back( io::path("../../media/icon_crosshairs16x16bw2.png") ); SCursorSprite spriteBw; // lo sprite + informazioni aggiuntive per il cursore spriteBw.SpriteId = AddAnimatedIconToSpriteBank( SpriteBankIcons, driver, files, 200 ); spriteBw.SpriteBank = SpriteBankIcons; spriteBw.HotSpot = position2d<s32>(7,7); context.addIcon(L"crosshair_bw", spriteBw);
// create one animated icon from one file array< rect<s32> > iconRects; iconRects.push_back( rect<s32>(0,0, 16, 16) ); iconRects.push_back( rect<s32>(16,0, 32, 16) ); iconRects.push_back( rect<s32>(0,16, 16, 32) ); iconRects.push_back( rect<s32>(0,16, 16, 32) ); iconRects.push_back( rect<s32>(16,0, 32, 16) ); SCursorSprite spriteCol; // lo sprite + informazioni aggiuntive per il cursore spriteCol.SpriteId = AddAnimatedIconToSpriteBank( SpriteBankIcons, driver, io::path("../../media/icon_crosshairs16x16col.png"), iconRects, 200 ); spriteCol.HotSpot = position2d<s32>(7,7); spriteCol.SpriteBank = SpriteBankIcons; context.addIcon(L"crosshair_colored", spriteCol);
// Creo alcune icone non animate rect<s32> rectIcon; SCursorSprite spriteNonAnimated(SpriteBankIcons, 0, position2d<s32>(7,7));
rectIcon = rect<s32>(0,0, 16, 16); spriteNonAnimated.SpriteId = AddIconToSpriteBank( SpriteBankIcons, driver, io::path("../../media/icon_crosshairs16x16col.png"), rectIcon ); context.addIcon(L"crosshair_col1", spriteNonAnimated, false);
rectIcon = rect<s32>(16,0, 32, 16); spriteNonAnimated.SpriteId = AddIconToSpriteBank( SpriteBankIcons, driver, io::path("../../media/icon_crosshairs16x16col.png"), rectIcon ); context.addIcon(L"crosshair_col2", spriteNonAnimated, false);
rectIcon = rect<s32>(0,16, 16, 32); spriteNonAnimated.SpriteId = AddIconToSpriteBank( SpriteBankIcons, driver, io::path("../../media/icon_crosshairs16x16col.png"), rectIcon ); context.addIcon(L"crosshair_col3", spriteNonAnimated, false);
MyEventReceiver receiver(context); device->setEventReceiver(&receiver);
while(device->run() && driver) { // if (device->isWindowActive()) { u32 realTimeNow = device->getTimer()->getRealTime();
context.update();
driver->beginScene(true, true, SColor(0,200,200,200));
env->drawAll();
// disegno lo sprite personalizzato con le funzioni di Irrlicht functions per confrontare. //Dovrebbe assomigliare al cursore. if ( context.SpriteBox ) { s32 selectedSprite = context.SpriteBox->getSelected(); if ( selectedSprite >= 0 && context.Sprites[selectedSprite].SpriteId >= 0 ) { SpriteBankIcons->draw2DSprite(u32(context.Sprites[selectedSprite].SpriteId), position2di(580, 140), 0, video::SColor(255, 255, 255, 255), 0, realTimeNow); } }
driver->endScene(); }
// Simuliamo un cattivo fps per capire se il supporto hardware dei cursori funziona o no. //Se funziona il cursore si muoverĂ normalmente, altrimenti lo aggiorniamo ogni 2 fps . if ( context.SimulateBadFps ) { device->sleep(500); // 2 fps } else { device->sleep(10); } }
device->drop();
return 0; }
Versione pdf scaricabile da QUI
|