GUI Scene managment

ShiftZ

18-11-2010 17:37:40

Hi, i wanted to clear some points. How it is supposed to deal with multi gui scenes. For instance, i want to load all resources needed (textures, skins) for a set of gui scenes, to run over them without reloading resources everytime i back to previous scene. And at some point i know that i wont ever return to some scenes, so i want to unload all textures referenced by these scenes.
What is the correct way to implement load\unload show\hide scenes?

Altren

23-11-2010 02:24:09

All resources (xml files) from core.xml loaded when MyGUI::Gui::initialise called. Textures loaded at moment when they used 1st time (when widget or cursor created).
To load additional skin or any other gui resource or set of resources you can useMyGUI::ResourceManager::getInstance().load("EditorSettings.xml");File may contain any type of MyGUI resources or list of files to load (like core.xml).

Any loaded layout file file is saved in memory and next time you call loadLayout function it is loaded from memory, not file.
We never encountered cases when you need to unload any GUI resources, even in very big applications (like MMORPG).

To load texture manually (instead of waiting for GUI to do that) use next code
MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().createTexture(textureFileName);
texture->loadFromFile(_textureFileName);
to unload it
MyGUI::RenderManager& render = MyGUI::RenderManager::getInstance();
render.destroyTexture(render.getTexture(textureName));

But I see no reasons to do that on PC, because usually GUI use only two textures (skin and font) or few more, so it's OK to keep them all loaded in memory.

And about layouts - me and my.name usually use classes derived from wraps::BaseLayout (you can find them in most demos and tools) and each class represent layout with single main widget. Usually it's easier to hide/show this main widget instead of destroying all widgets and then creating them again. But if your gui scenes are extremely heavy (more than 1000 widgets I guess) then you may want to destroy it.

ShiftZ

23-11-2010 13:45:30

But I see no reasons to do that on PC
My target is portable devices - iOS, Android. I dont know any cross-platform portable gui engine, so i have to deal with PC targeted.

ShiftZ

23-11-2010 20:26:04

To load texture manually (instead of waiting for GUI to do that) use next code
to unload it...


Looks like currently this is the only way how i can unload unnecessary textures.
But i have implemented a little helper MyGUI::ResourceManager::unload( std::string _file )
What it supposed to do is undo things that have been done by MyGUI::ResourceManager::load( std::string _file ).
However, it doesnt work because objects like ImageSetReousrce and ResourceSkin, do not unload textures that were loaded during MyGUI::ResourceManager::load();

Is there a practical reason not to remove related texture on destroy?
if no, can i add it?

my.name

24-11-2010 12:03:41

Ogre::TextureManager::getSingleton().getByName("texture name")->unload();

ShiftZ

24-11-2010 12:53:30

Ogre::TextureManager::getSingleton().getByName("texture name")->unload();

What did you meant by that?

gameengineer

07-12-2010 05:15:43

I was going to ask this exact question but it looks like someone addressed it already. Nice. :) I was interested in loading all of the GUIs (ie. menus) for the whole game at startup. Using Ogre there seemed to be a problem re-initializing a scenemanger and renderwindow for different game states was a problem. I am using Ogre's Advanced Framework which loads and destroys a scenemanager for each game state. A menu is a game state and so it the "gamestate" game state.
I do not know if that is the best way to do state management in Ogre but its what I'm working with for the time being.

That said is there a way to change the scene manager without causing the assertion? If not I will have to modify the framework to perhaps use a single scenemanager and clear or hide its contents during state changes.

Steve

Altren

07-12-2010 06:45:46

Every time you change scene manager you should tell MyGUI about it. And also reset it before scene manager destruction.
Something like that:
(MyGUI::OgrePlatform*)mPlatform->getRenderManagerPtr()->setSceneManager(nullptr);
// ... destroy old scene manager
// ... create new one
(MyGUI::OgrePlatform*)mPlatform->getRenderManagerPtr()->setSceneManager(mSceneManager);

gameengineer

07-12-2010 15:26:25

I didn't know we could do that. So we could create and initialize the MyGUI::Gui and MyGUI::OgrePlatform (assuming using the Ogre platform) at app start and then just change the scenemanager anytime we need to and it won't reload resources?

I just tried it and it worked! I thought we'd have to re-initialize every time we changed the scene manager and/or render window because of the required parameters of OgrePlatform::initialize().

So at this point we create the MyGUI and OgrePlatform instances in the Menu state so moving from the "title screen state" to the menu state has a noticeable delay where MyGUI is loading. I think I'll move the MyGUI creation to the app start. Now looking at the setRenderWindow() function, which takes pointers to the Ogre::RenderWindow and Ogre::SceneManager, we can pass a null for the pointer for the Ogre::SceneManager when initializing, right? I want to initialize early in app start after the render window is created so I can show a title screen but before the menu state is created. During the title screen I load everything (show a progress bar, that sort of thing).

It appears that the OgrePlatform::initialize() function simply passes the pointers through and its used to add the OgrePlatform pointer to the render queue.

void OgreRenderManager::setSceneManager(Ogre::SceneManager* _scene)
{
if (nullptr != mSceneManager)
{
mSceneManager->removeRenderQueueListener(this);
mSceneManager = nullptr;
}

mSceneManager = _scene;

if (nullptr != mSceneManager)
{
mSceneManager->addRenderQueueListener(this);
}
}


If the Ogre::SceneManager pointer happens to be nullptr then the OgrePlatform just doesn't get added.

I'll try this out.

Sorry, ShiftZ, I hijacked your thread a little. :roll:
Steve

gameengineer

07-12-2010 15:42:03

Ok so that worked. The transition from title to menu is fast now but I lost the button hover highlights for some reason. The handlers work and the pressed state shows but not the hover.

At app start I do this


void OgreFramework::createGui()
{
m_pOgrePlatform = new MyGUI::OgrePlatform();
m_pGUI = new MyGUI::Gui();

if (m_pOgrePlatform)
{
m_pOgrePlatform->initialise(m_pRenderWnd, nullptr);
}

// Create the GUI
if (m_pGUI)
{
m_pGUI->initialise();

MyGUI::LanguageManager::getInstance().loadUserTags("core_theme_black_blue_tag.xml");
OgreFramework::getSingletonPtr()->m_pGUI->load("core_skin.xml");

// Now load from a layout file
MyGUI::LayoutManager::getInstance().load("primary.layout");
}
}


At Menu State "enter" state I do this:


m_pOgrePlatform = OgreFramework::getSingletonPtr()->m_pOgrePlatform;
if (m_pOgrePlatform)
m_pOgrePlatform->getRenderManagerPtr()->setSceneManager(m_pSceneMgr);

// Move this to the app starting sequence
m_pGUI = OgreFramework::getSingletonPtr()->m_pGUI;
assert(m_pGUI);
if (m_pGUI)
{
// Now load from a layout file
MyGUI::LayoutManager::getInstance().load("primary.layout");

// Only add the buttons handlers once
if (!m_bIsGUIInitialized)
{
m_bIsGUIInitialized = true;

try
{

for (int i = 0 ; i < MAX_BUTTONS; i++)
{
// Set callback handler(s)
m_pButton[i] = m_pGUI->findWidget<MyGUI::Button>( gBUTTON_NAMES[i] );
if (m_pButton[i])
m_pButton[i]->eventMouseButtonClick = MyGUI::newDelegate(this, &MenuState::MyGUIEventHandler);
}

}
catch(...)
{

}
}
}


The Menu State button handlers get delegates set once the first time the menu is shown. I might even do this at app start and make all buttons global.

Not sure why the hover highlights aren't working.

EDIT: Could it be a eventSetMouseFocus or eventLostMouseFocus issue? If so how do I fix it?


Steve

gameengineer

07-12-2010 18:11:26

I fixed the problem but do not know why it was causing the problem.

By accident I was calling MyGUI::LayoutManager::getInstance().load("primary.layout"); twice. Once during app startup and another time in the MenuState::enter() method.

What is the difference between LayoutManager::load() and LayoutManager::loadLayout() (apart from the parm list) ?

Steve

gameengineer

07-12-2010 19:48:11

Still struggling. Basically what we need to do is this, and I am still new at MyGUI so this may not be the intended way to do it. We need to have at least two or three different layouts, perhaps defined in .layout files. I've already done this. There are currently 3 states that need to show their respective layout. Each state has its own Ogre::SceneManager because that's the way the AdvancedFramework has it.

MenuState - 4 buttons (Play, Instructions, Credits, Quit)
PauseState - 3 buttons (Continue, Return to Menu, Quit)
GameState - HUD display made up of various gui things (maybe buttons also)

So without getting into my implementation how do you show one layout and enable the button handlers, etc then clear that layout and show a completely different one with its own handlers? I'd am looking through the demos but unfortunately the code is intermixed with complicated base code. (yes, still a gripe of mine)

Is LayoutManager::unloadLayout the choice? I want to be able to always go back to the menu layout if they choose to so this seems too destructive and I would have to reconstruct the previously constructed menu layout.


Steve

Altren

07-12-2010 20:21:00

LayoutManager::load and LayoutManager::loadLayout are almost same, but loadLayout also return list of created root widgets. loadLayout is preferrable for use.
To handle groups of widgets we usually use one invisible panel with all widgets on it and show/hide it when necessary. And one class that control this panel and have all events hanlers and panel related code. This class assign events and load it's own layout on creation, and destroy it when class instance destroyed.

gameengineer

07-12-2010 22:28:17

That's a good idea too. I found another thread from someone who was experiencing the same issues and the suggestion there was to unload the layout ( unloadLayout() ) at game state exit and load at game state enter. That is working at the moment.

I assume when you say invisible panel you mean a Panel widget with its Alpha set to 255. I also assume that show/hide also disables the event handlers when hidden.

Your suggestion may be the fastest of both options in that there will be literally no loading and unloading of guis during state transitions. We can create all guis at app startup and incur the delay at that time.

Steve

Altren

08-12-2010 07:16:59

I assume when you say invisible panel you mean a Panel widget with its Alpha set to 255.Well, just use Widget with "PanelEmpty" skin as parent for other widgets.
I also assume that show/hide also disables the event handlers when hidden.Of course hidden widgets doesn't respond on mouse/keyboard.

gameengineer

08-12-2010 15:18:52

Of course hidden widgets doesn't respond on mouse/keyboard.
Don't assume. I have used a different commercial UI library where this was not the case. Hidden elements still responded, hidden and disabled did not. It didn't make sense to me but that's the way they did it.

Thanks for the fast reply. Other threads aren't so active, I am glad this one is. I plan on continuing to use this gui library.
Maybe in the not so distant future I will come up with some very simple single file demos to show some of the basic capabilities to help get newbies (like myself) hitting the ground running.

Steve

gameengineer

08-12-2010 23:02:45

Ok I'm using PanelEmpty but cannot seem to make it invisible. I do not want to see that transparent green color, I need it completely transparent. Setting alpha also makes the buttons that are children also transparent.
How do you make a complelely transparent Panel that contains child elements such as buttons?

Steve

Altren

09-12-2010 03:57:47

PanelEmpty is invisible in runtime, you can see that in Test mode of Layout Editor. It's green only in edit mode, because of usability, to see size of invisible widget (same transparent colours used for StaticImage and StaticText in edit mode).

gameengineer

16-12-2010 21:19:58

Another question. My main menu has a couple of other menus that have layouts. For example I have a multiplayer login layout. Is it the case where when entering my MainMenuState I can load all layouts and assign all button for the widgets that exist on all layouts? Then I would show/hide the particular layout when needed. Is this a common thing to do? Basically I want to keep the same scene and just display different menus rather than creating separate game states. I use one handler in my MainMenuState class so therefore I would add all button handlers there even though the "panel" of widgets may not be showing at the moment.

Steve

gameengineer

16-12-2010 21:54:18

I'll answer my own question. YES, what I asked about worked. :)

This makes it easier. I created 3 layout files using the LayoutEditor. (nice, handy little editor) When entering the MainMenuState of the statemachine I loaded all 3 layouts. I also saved the vector of widgets to their own std::vector variable. I did that to point to them to unload when exiting the state. Also when entering the state I found all buttons and delegated my single handler method. In that method I handle all events for all widgets on all panels. Oh yeah when creating the layouts I made sure all the widgets were children of another widget. In two cases the parent was a WindowC.

// Now load from a layout file
m_pMainWidgets = MyGUI::LayoutManager::getInstance().loadLayout("mainmenu.layout");

for (int i = eBTN_MAIN_PLAY; i <= eBTN_MAIN_QUIT; i++)
{
// Set callback handler(s)
m_pButton[i] = m_pGUI->findWidget<MyGUI::Button>( gBUTTON_NAMES[i] );
if (m_pButton[i])
m_pButton[i]->eventMouseButtonClick = MyGUI::newDelegate(this, &MenuState::MyGUIEventHandler);
}

// Find and show the main menu panel
m_MainMenuPanel = m_pGUI->findWidget<MyGUI::Widget>("panelMainMenu", false );
if (m_MainMenuPanel)
m_MainMenuPanel->setVisible(true);


And an example of how I loaded the login layout.

m_pLoginWidgets = MyGUI::LayoutManager::getInstance().loadLayout( "login.layout" );

for (int i = eBTN_LOGIN_HOST_GAME; i <= eBTN_LOGIN_BACK_TO_MENU ; i++)
{
// Set callback handler(s)
m_pButton[i] = m_pGUI->findWidget<MyGUI::Button>( gBUTTON_NAMES[i] );
if (m_pButton[i])
m_pButton[i]->eventMouseButtonClick = MyGUI::newDelegate(this, &MenuState::MyGUIEventHandler);
}

m_LoginPanel = m_pGUI->findWidget<MyGUI::Widget>("login", false );
if (m_LoginPanel)
m_LoginPanel->setVisible(false);



By the way can I have a WindowC widget that is fixed or unmovable? Maybe that defeats the purpose but I like the widget title bar I just don't want it to be able to be moved around. Maybe there's another widget for this.

In the near future I need to learn how to create my own graphics and use them instead of the "core" graphics.

Steve

ShiftZ

16-12-2010 22:09:26

I have a wrapper called MyGuiScene that holds vector of loaded widgets and methods to hide\show all of them.
Btw, I'd prefere guarentee that every mygui layout scene has a single root canvas widget insted of that too liberal form when widgets are on their own.

gameengineer

16-12-2010 23:36:17


Btw, I'd prefere guarentee that every mygui layout scene has a single root canvas widget insted of that too liberal form when widgets are on their own.

Good point. I originally created my layouts with "free floating" widgets, widgets with no containing parent. After reading this thread and asking a few questions it quickly turned out to be better to have owing parent widgets to a group of children widgets.

Steve