CEGUI In Practice - A Game Console
Written for CEGUI 0.7
Works with versions 0.7.x (obsolete)
CEGUI In Practice series
CEGUI InPractice 6
Welcome back! This tutorial we will put together the knowledge from the past few chapters to create a functioning Console window. The last few tutorials have given us the basics. Now its time to put that knowledge to use and make something we can use in our game.
Game Plan
Okay, now lets look at what we need to do. We will want to be able to type in commands, press the send button, and have them output into the text box. We will also probably want to acknowledge when enter is pressed as thats the 'normal' feel of typing into a chat.
We discussed earlier how to send input to CEGUI (CEGUI In Practice - Managing input), so we will have to assume that you have that working (and probably built the application structure behind it). We will also assume you used the previous tutorial's layout file and naming for the console window ( CEGUI In Practice - Using .layout files).
Due to the fact that we are getting to a slightly more advanced implementation of CEGUI here, and because we may want the ConsoleWindow to do other more useful things (like parsing strings for /say commands) I'm going to create a class to encompass the CEGUI ConsoleWindow.
class GameConsoleWindow { public: GameConsoleWindow(); // Constructor private: void CreateCEGUIWindow(); // The function which will load in the CEGUI Window and register event handlers void RegisterHandlers(); // Register our handler functions void Handle_TextSubmitted(const CEGUI::EventArgs &e); // Handle when we press Enter after typing void Handle_SendButtonPressed(const CEGUI::EventArgs &e); // Handle when we press the Send button void ParseText(CEGUI::String inMsg); // Parse the text the user submitted. void OutputText(CEGUI::String inMsg); // Post the message to the ChatHistory listbox. CEGUI::Window *m_ConsoleWindow; // This will be a pointer to the ConsoleRoot window. CEGUI::String sNamePrefix; // This will be the prefix name we give the layout static int iInstanceNumber; // This will be the instance number for this class. };
Alright, so that will be our class. It might look daunting, but don't worry it will all make sense in the end.
The Constructor
I'm going to use the constructor as the main point of automation here. And while I believe it is normally bad practice to call functions which could potentially fail within a constructor, i'm going to anyway for ease of this tutorial. We will have it Initialize some variables, and then call the CreateCEGUIWindow function.
GameConsoleWindow::GameConsoleWindow() { m_ConsoleWindow = NULL; // Always good practice to initialize a pointer to a NULL value, helps when switching to Release Builds. iInstanceNumber = 0; sNamePrefix = ""; }
Setting up the Window
Now we need to setup the window. We have done this before in the .layout tutorial so we will do it again here. However, we mentioned before that you could use a prefix when loading a .layout to avoid name conflicts if you load multiple layouts. So we need to account for that. Will we need multiple consoles? Who knows, might as well build it in while we're designing it right?
Lets write the CreateCEGUIWindow function:
void GameConsoleWindow::CreateCEGUIWindow() { // Get a local pointer to the CEGUI Window Manager, Purely for convenience to reduce typing CEGUI::WindowManager *pWindowManager = CEGUI::WindowManager::getSingletonPtr(); // Now before we load anything, lets increase our instance number to ensure no conflicts. // I like the format #_ConsoleRoot so thats how i'm gonna make the prefix. This simply // Increments the iInstanceNumber then puts it + a _ into the sNamePrefix string. sNamePrefix = ++iInstanceNumber + "_"; // Now that we can ensure that we have a safe prefix, and won't have any naming conflicts lets create the window // and assign it to our member window pointer m_ConsoleWindow m_ConsoleWindow = pWindowManager->loadWindowLayout(inLayoutName,sNamePrefix); // Being a good programmer, its a good idea to ensure that we got a valid window back. if (m_ConsoleWindow) { // Lets add our new window to the Root GUI Window CEGUI::System::getSingletonPtr()->getGUISheet()->addChildWindow(m_ConsoleWindow); // Now register the handlers for the events (Clicking, typing, etc) (this)->RegisterHandlers(); } else { // Something bad happened and we didn't successfully create the window lets output the information CEGUI::Logger::getSingletonPtr()->logEvent("Error: Unable to load the ConsoleWindow from .layout"); } }
Alright so that created the window. And after the window was created, we Registered its handlers. Lets look at how we go about registering those handlers. Below is the RegisterHandlers function, it will probably look familiar if you have been reading along:
void GameConsoleWindow::RegisterHandlers() { // Alright now we need to register the handlers. We mentioned above we want to acknowledge when the user presses Enter, and // when they click the 'Send' button. So we need to register each of those events // First lets register the Send button. Our buttons name is "ConsoleRoot/SendButton", but don't forget we prepended a name to // all the windows which were loaded. So we need to take that into account here. m_ConsoleWindow->getChild(sNamePrefix + "ConsoleRoot/SendButton")->subscribeEvent( CEGUI::PushButton::EventClicked, // If we recall our button was of type CEGUI::PushButton in the .scheme // and we want to acknowledge the EventClicked action. CEGUI::Event::Subscriber( // What function to call when this is clicked. Remember, all functions // are contained within (this) class. &GameConsoleWindow::Handle_SendButtonPressed, // Call Handle_SendButtonPressed member of GameConsoleWindow this)); // Using (this) instance we're in right now // Now for the TextSubmitted, we will be registering the event on the edit box, which is where the users cursor will be when //they press Enter. I'm not going to break this down as much, because I believe that is very ugly to read, but was a good //way of expressing it. Here is the function call. m_ConsoleWindow->getChild(sNamePrefix + "ConsoleRoot/EditBox")->subscribeEvent(CEGUI::Editbox::EventMouseClick, CEGUI::Event::Subscriber(&GameConsoleWindow::Handle_TextSubmitted,this)); }
That last line looks pretty ugly, but remember if you include namespace CEGUI; you won't have all those Ugly CEGUI:: prefixing all the code. As you can see, once you get the hang of registering events its pretty easy. One thing to note, is there are more than one way to account for certain actions. On the button, you could also have registered for:
CEGUI::PushButton::EventMouseClick CEGUI::PushButton::EventMouseButtonDown CEGUI::PushButton::EventMouseButtonUp
Depending on what result you were looking for, and where you wanted the action to take place. For example, windows interfaces usually react on the MouseButtonUp, allowing the user to click it, and then slide off the mouse if it wasn't what they really wanted to press.