The Making of WarpTris - Part 2Written by Paulo Gago da Camara |
IntroductionWelcome to the second installment of "The making of WarpTris". As promised, this month I'll cover the PM portion of the game.The User InterfaceThe WarpTris User Interface (UI) was completely written using the Presentation Manager (PM) API; after all, the main purpose of this game was for me to learn PM programming.I chose to use a multi-window interface, because it gives the user more freedom on how to display the different information of the game. Before describing the details of the PM implementation, I'm going to explain how the digital panel that shows the score is implemented. Creating a Digital PanelThe scores digital panel is implemented as a class called 'DigitalPanel'. This class is declared and its methods are defined inside the 'Digital.h' file.The graphical digits are 16 color bitmaps with a size of 11x21 pixels each. I had to draw each digit separately for the numbers '0' to '9' and an additional 'Off digit' which is used where no digit is lit. When an object of this class is created, the constructor loads all these bitmaps using the GpiLoadBitmap API into an array of 'HBITMAP', which is used later to draw the necessary digit. Now I'll give a description of all the methods and data members of the class 'DigitalPanel'.
class DigitalPanel { private: HWND hwndOwner; // Handle of the window that owns the digital panel HBITMAP hbmp[11]; // The handles of the bitmaps that represents the numbers INT digitCount; // The number of digits that the display has INT posX, posY; // Coordinates of the lower left corner ULONG value; // The value that it currently displays VOID LightDigit(HPS hps, INT digit, INT v); // Light a given digit with // a given value(0..9) public: DigitalPanel(HWND hwnd, INT x, INT y, INT digitC, ULONG v=0); // Constructor ~DigitalPanel(); // Destructor VOID Draw(BOOL showValue=TRUE); // Draw the panel VOID ShowValue(VOID); // Show the value on it VOID SetValue(ULONG v) {value=v;}; // Set the value VOID AddToValue(ULONG v) {value += v; Draw();} // Add a number to the value ULONG GetValue(VOID) {return(value);} // Get the value };It is very easy to use this class in any other PM program, so feel free to use it any time. WarpTris Windows and Window ProceduresThere are eight windows and window procedures in WarpTris. Some of them are visible to the user and others aren't. In this section I'll describe each window and associated window procedure separately. We will also take a look at some of the most important messages processed by them.One of the window concepts that I used extensively in WarpTris was 'Window Words'. 'Window Words' are nothing more that a given number of bytes that are reserved when we create a window. This storage is private to each instance of a window class and it's normally used to store a pointer (or more) to a data structure, which in turn holds all the data we need to associate with that window. The amount of storage we want to reserve for 'Window Words' is specified in the fifth parameter of the WinRegisterClass API. For a pointer, this value should be four bytes (32 bits). Later we can initialize and read the pointer stored in the "Window Words" using the WinSetWindowPtr and WinQueryWindowPtr APIs, respectively. Several structures are declared on the 'WarpTris.cpp' file, these structures store relevant information about the windows to which they are associated using the 'Window Words' mechanism described above. When a window is being created, the first message it receives is the WM_CREATE message. This message is normally used to initialize the data needed for that window. I used this message on all windows to allocate and initialize the associated data structures and to store a pointer to them in the corresponding 'Window Words'. Later, when the window is destroyed it receives the WM_DESTROY message, and the storage allocated for these structures is freed inside that message processing. Now I'll start describing each WarpTris window procedure. MRESULT EXPENTRY GameWindowProc(HWND hwnd, ULONG msg, MPARAM mp1,MPARAM mp2)This is the prototype of the window procedure that handles the events for the game window. This function processes all the messages that are generated by the user interaction when playing the game. This is also where the objects of class Board and Figure, which were described in an earlier article, are used. Now I'll give a small description of the most important messages processed by this window procedure.
MRESULT EXPENTRY LogoWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)This is the window procedure for the logotype that appears when the game is loading. The processing of this window is rather simple, it just shows the WarpTris logotype bitmap for a few seconds or until the user presses a key or clicks on it. MRESULT EXPENTRY MainWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)This window procedure handles the events for the main WarpTris window. The main purpose of this window is to serve as a parent for all the other windows of the game. On creation, this window procedure reads the settings of the game and initializes all the menus and windows accordingly. These settings and properties are stored in the WarpTris.ini file.
MRESULT EXPENTRY NextWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);This is the window procedure for the window that displays the next tile and the next direction. This window shares a data structure with the game window. That data structure is used to hold the next tile object and its impulse direction. When a new tile is needed on the game window, the game window procedure reads it from the shared data structure using the overloaded operator '=', and posts a WM_NEXT message to this window procedure, which in turn creates a new figure object and a new direction randomly, to be used the next time the game window needs a new tile. MRESULT EXPENTRY ScoreWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);This is the window procedure for the window that displays the score digital panel. It exists just to hold the view of an object of the DigitalPanel class described earlier on this article. There's nothing to know about this window procedure, all the relevant processing is done by the DigitalPanel class that was described earlier. This window also has access to the shared data structure described above which holds an object of the DigitalPanel class. MRESULT EXPENTRY HighWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);This is the window procedure that handles the events for the 'High Scores' window. This window simply creates a list box control that is filled with an object of the HighScore class, which exists on the shared data structure that it also has access to. MRESULT EXPENTRY NoteBookWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);This is the window procedure for the window that is used to hold the properties notebook control. When this window is first created it creates the notebook control and initializes it according to the properties saved in the 'WarpTris.ini' file. The notebook control has three pages. The dialog procedures for the dialogs that make up the pages are the following:
MRESULT EXPENTRY ObjectWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);This is the window procedure for the object window that is used by the sound thread. An 'Object Window' is a window that doesn't have a graphical representation and its normally used to assist other windows in their processing or interaction with other objects. The 'Object Windows' only have two system defined messages which are the WM_CREATE and WM_DESTROY messages. What the programmer normally does is to define a set of user messages to handle the processing needed on a particular situation. On WarpTris I've used this concept to manage the messages that ask the sound thread to play the sound effects that are used during the game. Playing the Sounds on a separate threadI decided to play the sound effects on a separate thread of execution, because if they were played on the same thread as the game runs on, it would slow down the tile movement. This wouldn't be a Good Thing(tm)Because of my weak knowledge of the OS/2 multimedia API, I was forced to use the MCI command string interface which is quite simple to use but obviously has its limitations. What happens is that, when a sound needs to be played, for example the pop sound, the game window posts the WM_POP user defined window message to the thread object window which in turn plays the selected sound effect. ConclusionWell, this is it. I hope that reading this article pleased you as much as it did writing it. If you have any question or suggestion about the game or this article, you're welcome to E-mail me with it, and I'll do my best.My native language is Portuguese, so please forgive me if my English is poor. Until next time! |