Blame |
Last modification |
View Log
| RSS feed
#ifndef __BOARD_H__
#define __BOARD_H__
//////////////////////////////////////////////////////////////////////////
// Board.h
//
// This is the third class to look at in this particular demo
// (after main.cpp and GameApp.h/.cpp). The Board class is where most of
// your actual game programming will go. It is here that we will do
// all our game drawing, updating, and input processing. Of course, in
// a larger application, you would probably do drawing and updating in
// multiple files, but you would still most likely use something similar
// to a Board class as the master game logic class.
//
// The reason that the Board class is a widget is because when a widget
// is added to the GameApp's WidgetManager, it will automatically have its
// Update and Draw methods called, and it will automatically receive input
// at the appropriate times. Furthermore, by making it a widget and adding
// it to the WidgetManager, the game logic loop, Update(), will be guaranteed
// to run at a standard 100FPS on all machines. This is extremely important
// as you always want your logic code to run at the same speed, but want
// the drawing code to run as fast as possible. That way on faster machines
// your program doesn't run its logic faster than on a slower machine.
//
// You can think of the Board as a canvas upon which we do all our
// drawing, and a central hub where if we need to, we instruct other
// classes where and when to draw to.
//////////////////////////////////////////////////////////////////////////
#include "SexyAppFramework/Widget.h"
#include "SexyAppFramework/ButtonListener.h"
// Because we're going to be learning about some new widgets, we
// need to include some more listener classes so we can respond to each one.
#include "SexyAppFramework/EditListener.h"
#include "SexyAppFramework/CheckboxListener.h"
#include "SexyAppFramework/ListListener.h"
// We place all our classes inside the "Sexy" namespace to avoid name collisions
// with other libraries that might be added.
namespace Sexy
{
// Forward declare the graphics class. You will see the graphics class used
// and explained in Board.cpp: it is the main object used to draw all
// images, fonts, etc.
class Graphics;
// We maintain a pointer to the main game application in the Board class.
// The main game app contains functions that are often times needed
// by the Board class, such as registry reading/writing, file reading/writing,
// etc.
class GameApp;
// forward declare the widgets we're going to use in this demo:
class ButtonWidget;
class EditWidget;
class Checkbox;
class ListWidget;
class ScrollbarWidget;
// In this demo, we're going to do some more advanced things like
// handle the two cases where Board is added and removed from the
// WidgetManager.
class WidgetManager;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class Board : public Widget, public ButtonListener,
public EditListener, public CheckboxListener,
public ListListener
{
private:
GameApp* mApp;
ButtonWidget* mButton1; // We'll use these buttons for sound playing
ButtonWidget* mButton2;
// These are explained in the C++ code, they are the new widgets we're learning about.
EditWidget* mEditWidget;
Checkbox* mCheckboxWidget;
ListWidget* mListWidget;
ScrollbarWidget* mScrollbarWidget;
SexyString mText; // When we press enter on the edit box, we'll set this string and print it
// Both are floats to ensure that the only difference in the movement demo
// is the fact that one is updated in UpdateF and the other is in Update.
float mMotionX; // For our movement example, this is the X coordinate of the image as it moves rightward
float mUpdateFMotionX;// Same as above, but only modified in UpdateF, to illustrate the difference in motion
public:
//////////////////////////////////////////////////////////////////////////
// Function: Board
// Parameters:
// theApp - Pointer to the main application class
//
// Returns: none
//////////////////////////////////////////////////////////////////////////
Board(GameApp* theApp);
virtual ~Board();
//////////////////////////////////////////////////////////////////////////
// Function: EditWidgetText
// Parameters:
// theId - Integer ID of the edit widget sending this message
// theString - The contents of the edit widget
//
// Returns: none
//
// Purpose: Called whenever the return/enter key is pressed on
// an edit widget.
//////////////////////////////////////////////////////////////////////////
void EditWidgetText(int theId, const std::string& theString);
//////////////////////////////////////////////////////////////////////////
// Function: AllowChar
// Parameters:
// theId - Integer ID of the edit widget sending this message
// theChar - Character just typed in
//
// Returns:
// true - Indicates that the character is acceptible
// false - Indicates that the character is invalid
//
// Purpose: Whenever an ASCII character is typed into the edit box,
// this method is called first. If the method returns true, then the
// character just typed is accepted and appended to the current edit widget
// string. If it returns false, the character is rejected and is not added.
//////////////////////////////////////////////////////////////////////////
bool AllowChar(int theId, char theChar);
//////////////////////////////////////////////////////////////////////////
// Function: CheckboxChecked
// Parameters:
// theId - Integer ID of the checkbox widget sending this message
// checked - Boolean indicating if the widget is checked or not
//
// Returns: none
//
// Purpose: Whenever a checkbox widget is checked or unchecked, this
// method is called. We're not actually going to do anything with this,
// we're just listing it here as an example of how you'd implement a
// function to respond to that event.
//////////////////////////////////////////////////////////////////////////
void CheckboxChecked(int theId, bool checked) {;}
//////////////////////////////////////////////////////////////////////////
// Function: ListClicked
// Parameters:
// theId - Integer ID of the listbox widget sending this message
// theIdx - Integer indicating the index of the item selected in the list
// theClickCount - An integer indicating which mouse button
// was pressed. One of the following:
// 1: Left button
// 2: Double-left-click
// 3: Middle button
// -1: Right button
// -2: Double-right-click
//
// Returns: none
//
// Purpose: Called any time a list widget is clicked on. The list
// widget by default doesn't automatically select the item you clicked on,
// it instead calls this method and in here you manually select the item.
// This is to allow you to prevent the selection of certain items, such as
// disabled items, etc.
//////////////////////////////////////////////////////////////////////////
void ListClicked(int theId, int theIdx, int theClickCount);
//////////////////////////////////////////////////////////////////////////
// Function: Draw
// Parameters:
// g - Graphics object used to draw all images and fonts to the screen.
//
// Returns: none
//
// Purpose: Called automatically by GameApp's WidgetManager, this function
// is the main method that is responsible for all graphical and textual
// displaying.
//////////////////////////////////////////////////////////////////////////
virtual void Draw(Graphics* g);
//////////////////////////////////////////////////////////////////////////
// Function: Update
// Parameters: none
// Returns: none
//
// Purpose: Called automatically by GameApp's WidgetManager, this method
// is GUARANTEED to be called 100 times per second (100FPS) and is where
// all main game logic is performed. Of course, if you had a larger more
// complex game, you'd most likely divide your logic between several
// other files, but this is commonly the central place where all game
// logic begins and is executed.
//////////////////////////////////////////////////////////////////////////
virtual void Update();
//////////////////////////////////////////////////////////////////////////
// Function: UpdateF
// Parameters:
// theFrac - The number of updates this time slice represents.
//
// Returns: none
//
// Purpose:
// There has been a fundamental temporal aliasing issue in the previous
// demos because games run at a 100 Hz Update rate while the user's monitor
// is refreshing at some other rate, generally between 60 and 85 Hz. The fixed
// 100 Hz Update rate is convenient because it allows game logic to proceed
// independantly from the refresh rate, but in some cases it's worth the extra
// trouble of updating at a variable rate in order to provide smoother animation,
// as in the case of a scrolling background, a shark with words written on it,
// or an Arkanoid ball.
//
// To illustrate the aliasing problem, imagine a ball that is supposed to move
// 200 pixels per second, running on a 75 Hz monitor. The update rate of the
// game is 100 Hz, so that means that we will add 2 pixels to the ball position
// every update, and there will be 1.33 updates per monitor refresh (on average).
// That means that that 2 out of every 3 monitor refreshes will show the ball
// moving 2 pixels, and and the third will show it moving 4 pixels. That isn't
// smooth motion. The correct solution would be for the ball to move 2.67
// pixels every monitor refresh. But how do we do that?
//
// To support smooth motion, we use UpdateF. Widget::UpdateF is similar to
// Widget::Update, but Widget::UpdateF gets a float passed into it that
// represents how many Update's this time slice represents. In the 75 Hz
// example, UpdateF would always be called with 1.33. Update has certainly
// not been made obsolete, however, and you can choose which
// parts of your game logic should be in Update and which should be in
// UpdateF. To facilitate cooperation and good behavior between the two
// update methods, there are some rules they follow: Updating always occurs
// in blocks, with one or two Update calls followed immediately with an
// UpdateF call. This means that the application will never get the chance
// to draw or process input between an Update and a Draw without calling
// UpdateF in the middle. Therefore, you can assume that focus won't be
// lost, nor will input change between an Update and an UpdateF, and you'll
// know that you'll have a chance to finalize your state in UpdateF so things
// can be left dangling (whatever that means for your app) after Update.
// You are also guaranteed that the value passed in to UpdateF will be between
// 1.67 (for a 60 Hz monitor) and 1.0 (for a 100 Hz monitor). Even if the
// monitor is 60 Hz but the computer is only fast enough to draw at 30 FPS
// you will get two Update blocks in a row before the draw, so it will still
// appear to your app as if you are updating at 60 Hz.
//
// IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
//
// In order to fully use this, you need to set up a few things.
// Set GameApp::mVSyncUpdates to true, override UpdateF(float theFrac),
// and move some code from Update that used to look like
// this: "mPos += 1.5;", changing it to "mPos += 1.5 * theFrac;".
// Check out the C++ code for an example of motion using both Update and
// UpdateF.
//
// IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
//
// Because UpdateF is called a variable number of times per second,
// you do NOT want to put game logic in it that needs to remain framerate
// independant. Use UpdateF ONLY for movement related operations, and not
// for your main game code.
//
// If you really want to avoid shearing in windowed mode, you can
// set GameApp::mWaitForVSync to true and set GameApp::mSoftVSyncWait
// to false. NOTE: This winds up doing some busy waiting and consumes
// more processor time.
// IMPORTANT: YOU MUST ALSO DELETE THE FOLLOWING REGISTRY KEY:
// Whereever your registry settings are stored
// (HKEY_LOCAL_MACHINE\SOFTWARE\SexyAppFramework\Demo4 for this case),
// you must delete the key "WaitForVSync". This is VERY important, and it
// won't work otherwise.
//////////////////////////////////////////////////////////////////////////
virtual void UpdateF(float theFrac);
//////////////////////////////////////////////////////////////////////////
// Function: ButtonDepress
// Parameters:
// theId - Integer ID of the button that was clicked
//
// Returns: none
//
// Purpose: This method is called by the WidgetManager when a button widget
// is first pressed and THEN released. You can use ButtonPress if you want
// to know when the button is first pressed (before it is released).
// theId is the integer ID that was assigned to the button when it was
// first created.
//////////////////////////////////////////////////////////////////////////
virtual void ButtonDepress(int theId);
//////////////////////////////////////////////////////////////////////////
// Function: AddedToManager
// Parameters:
// theWidgetManager - Pointer to the main widget manager from
// GameApp.
//
// Returns: none
//
// Purpose: This function is automatically called by the widget manager
// which also passes a pointer to itself, when the Board class is
// added to its list of widgets. Every widget gets this function
// called when it is first added. It useful to use this function to
// set up any other widgets that the class might contain, such as buttons.
//////////////////////////////////////////////////////////////////////////
virtual void AddedToManager(WidgetManager* theWidgetManager);
//////////////////////////////////////////////////////////////////////////
// Function: RemovedFromManager
// Parameters:
// theWidgetManager - Pointer to the main widget manager from
// GameApp.
//
// Returns: none
//
// Purpose: This function is automatically called by the widget manager
// which also passes a pointer to itself, when the Board class is
// removed from its list of widgets. Every widget gets this function
// called when it is finally removed. It useful to use this function to
// also remove any widgets that were added and created in AddedToManager.
//////////////////////////////////////////////////////////////////////////
virtual void RemovedFromManager(WidgetManager* theWidgetManager);
};
}
#endif // __BOARD_H__