CEGUI In Practice - A Game Console

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Revision as of 20:24, 4 March 2011 by Avengre (Talk | contribs)

Jump to: navigation, search

Written for CEGUI 0.7


Works with versions 0.7.x (obsolete)

CEGUI In Practice series

This page is part of a series, use links above to easily navigate through all chapters of the 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.

The Handlers

Now that we have registered the events, we need to actually write the functions which will handle those events. We need to handle both the Text Submitted, and the SendButton's event.

void GameConsoleWindow::Handle_TextSubmitted(const CEGUI::EventArgs &e)
{
    // The following line of code is not really needed in this particular example, but is good to show.  The EventArgs by itself 
     // only has limited uses. You will find it more useful to cast this to another type of Event.  In this case WindowEventArgs
     // could be much more useful as we are dealing with a CEGUI::Window.  Notably, this will allow you access to the .window
     // member of the argument, which will have a pointer to the window which called the event.  You can imagine that would be
     // useful!
    const CEGUI::WindowEventArgs* args = static_cast<const CEGUI::WindowEventArgs*>(&e);
 
    // Now we need to get the text that is in the edit box right now.
    CEGUI::String Msg = mWindow->getChild(sNamePrefix + "ConsoleRoot/EditBox")->getText();
 
    // Since we have that string, lets send it to the TextParser which will handle it from here
    (this)->ParseText(Msg);
 
}