*//*
Procedural Overview -- Items in bold are additions/changes to the previous lesson.
Comments explaining previously covered material will be made more terse or deleted entirely in each successive lesson. If something is not explained well enough, it was probably already explained in previous lessons.
Code Diving!
*/ // This header MUST be included in every source/header file. #include "xrb.hpp" #include "xrb_button.hpp" // For use of the Button widget class. #include "xrb_event.hpp" // For use of the Event classes. #include "xrb_eventqueue.hpp" // For use of the EventQueue class. #include "xrb_inputstate.hpp" // For use of the InputState class (via Singleton::). #include "xrb_label.hpp" // For use of the Label widget class. #include "xrb_layout.hpp" // For use of the Layout widget class. #include "xrb_lineedit.hpp" // For use of the LineEdit widget class. #include "xrb_screen.hpp" // For use of the necessary Screen widget class. #include "xrb_sdlpal.hpp" // For use of the SDLPal platform abstraction layer. // Used so we don't need to qualify every library type/class/etc with Xrb:: using namespace Xrb; // This is just a helper function to group all the shutdown code together. void CleanUp () { fprintf(stderr, "CleanUp();\n"); // Shutdown the platform abstraction layer. Singleton::Pal().Shutdown(); // Shutdown the game engine singletons. This is necessary for the // game engine to shutdown cleanly. Singleton::Shutdown(); } int main (int argc, char **argv) { fprintf(stderr, "main();\n"); // Initialize the game engine singleton facilities. Singleton::Initialize(SDLPal::Create, "none"); // Initialize the platform abstraction layer (Pal). if (Singleton::Pal().Initialize() != Pal::SUCCESS) return 1; // Set the window caption. Singleton::Pal().SetWindowCaption("XuqRijBuh Lesson 01"); // This call creates the Screen object and initializes the given video mode. // The Screen object is the root widget of the GUI widget hierarchy, and // does a bunch of special handling to draw its child widgets properly. Screen *screen = Screen::Create( 800, // video mode/screen width 600, // video mode/screen height 32, // video mode pixel bitdepth false); // not fullscreen -- none for now. // If the Screen failed to initialize, print an error message and quit. if (screen == NULL) { fprintf(stderr, "failure during Screen creation\n"); // this shuts down the Pal and singletons. CleanUp(); // return with an error value. return 2; } /*
Everything that is visible in this game engine happens inside an instance of a Widget subclass. Therefore, we need to instantiate some widgets before anything interesting can happen.
There are custom subclasses of Widget (such as Label, LineEdit, Button, Layout, etc) to perform various functions. Widgets are organized in a hierarchy (i.e. parent/child relationship) which also dictates the spatial organization of widgets onscreen -- a child widget is completely contained within its parent, and cannot draw anything outside itself. This is the same widget paradigm as used by the FLTK, MFC, QT (and many other) toolkits. The GUI system in this game engine is primarily modeled after Trolltech's excellent QT GUI toolkit.
Instead of having to worry about the exact screen coordinates at which to place our widgets, there is a Widget subclass called Layout which automatically handles widget sizing and placement. Layouts can place widgets in horizontal/vertical lines or in grids, and can be nested within one another to form complicated formatting of widgets.
*/
{
/*
*/ Layout *main_layout = new Layout(VERTICAL, screen, "main layout"); /*
*/
screen->SetMainWidget(main_layout);
/*
A Label is a simple, non-interactive widget which draws text or a picture (we'll get to picture labels later). the default justification for a Label's contents is centered both horizontally and vertically, but this, among other properties, can be changed.
*/ new Label("I LIKE ZOMBIES.", main_layout, "awesome zombie text label"); /*
*/ new Button("This button does nothing", main_layout, "do-nothing button"); /*
A LineEdit is a text-entry box. The first parameter in its constructor indicates the maximum number of characters that can be entered into it. It derives from the same baseclass as Label, so it shares many of the same properties as Label, such as alignment, text color, and so forth.
*/ { Layout *sub_layout = new Layout(HORIZONTAL, main_layout, "label and line-edit layout"); // Create a text Label to indicate what to do with the following LineEdit. new Label("You can enter up to 30 characters in this LineEdit ->", sub_layout, "indicator label"); // Create the LineEdit after the Label, and it will be placed next // in the horizontal layout. new LineEdit(30, sub_layout, "30-char line edit"); } /*
*/ { Layout *sub_layout = new Layout(HORIZONTAL, main_layout, "note label layout"); // Another text Label. It notes that holding down a key will not cause // the character to repeat. Doing this will be discussed later. This // Label demonstrates word wrapping and justification. Label *note_label = new Label( "Note that holding a key down while typing into the\n" "LineEdit does not cause more than one character to\n" "be entered (i.e. no keyboard repeating). Doing so\n" "complicates the event-polling loop, and it was\n" "omitted to keep this example as simple as possible.\n" "Notice how this text Label is wider than the other\n" "three adjacent Labels. This is because by default,\n" "text Labels' minimum width is fixed to the text\n" "width, so none of the text is cut off. The other\n" "three Labels can be resized horizontally because\n" "they each have word wrapping enabled, so the text\n" "formatting is dictated by the width of the Label.", sub_layout, "left-aligned note label"); // All text will be aligned with the left edge of the Label. By default, // a Label's alignment is CENTER. note_label->SetAlignment(Dim::X, LEFT); // Add another label, this time, with centered alignment. note_label = new Label( "This Label widget is using word wrapping with center alignment. " "The width of the Label dictates where the words will be wrapped.", sub_layout, "center-aligned note label with word wrapping"); // This call does exactly what it looks like it does. note_label->SetWordWrap(true); // The default alignment is already CENTER, so we don't need to do anything. // Add another label, this time, with right alignment. note_label = new Label( "This Label widget is using word wrapping with right alignment. " "Any Label (not just word wrapped Labels) can use the alignment " "property, including aspect-ratio-preserving picture Labels.", sub_layout, "right-aligned note label with word wrapping"); // This call does exactly what it looks like it does. note_label->SetWordWrap(true); // All text will be aligned with the right edge of the Label. note_label->SetAlignment(Dim::X, RIGHT); // Add another label, this time, with right alignment. note_label = new Label( "This Label widget uses word wrapping with character spacing. " "Spacing is a special type of alignment that only applies to word " "wrapped Labels, and will attempt to space the characters out to " "fill out the entire width of the Label. The extra spacing between " "characters seems to sometimes screw up the font's kerning (the " "space between specific glyph pairs to make the text look more " "natural).", sub_layout, "spaced note label with word wrapping"); // This call does exactly what it looks like it does. note_label->SetWordWrap(true); // see note_label's text for a description. note_label->SetAlignment(Dim::X, SPACED); } /*
We're done creating widgets for this example. Next up is the game loop, where all the computation, screen-rendering, event-processing, user-input-processing, etc takes place.
The screen keeps track of if a quit request was detected (i.e. Alt+F4 or clicking the little X in the corner of the window pane). We want to loop until the user requests to quit the app.
*/ while (!screen->IsQuitRequested()) { /*
*/
Singleton::Pal().Sleep(33);
/*
*/
Float time = 0.001f * Singleton::Pal().CurrentTime();
/*
*/ Event *event = NULL; while ((event = Singleton::Pal().PollEvent(screen, time)) != NULL) { /*
*/ if (event->IsKeyEvent() || event->IsMouseButtonEvent()) Singleton::InputState().ProcessEvent(event); /*
*/
screen->ProcessEvent(event);
/*
*/ Delete(event); } /*
*/
screen->OwnerEventQueue()->ProcessFrame(time);
/*
*/
screen->ProcessFrame(time);
/*
*/
screen->OwnerEventQueue()->ProcessFrame(time);
/*
*/ screen->Draw(); } } // Delete the Screen object, and with it the entire GUI widget hierarchy. Delete(screen); // this shuts down the Pal and the singletons. CleanUp(); // return with success value. return 0; } /*
Exercises
Singleton::Pal()
.Sleep call in the game loop, and see what effect it has on the responsiveness of the Button and LineEdit. main_layout
after the do-nothing button. Note that their on-screen position in the vertical main_layout
is directly reflected by the order they're created as children of main_layout
. "left-aligned note label"
. Notice how each newline in the Label's text starts a new paragraph. Thus concludes lesson01. Do you feel smarter? Well, you aren't.