Blame |
Last modification |
View Log
| RSS feed
#include "Board.h"
#include "GameApp.h"
// Contains all the resources from the resources.xml file in our
// properties directory. See that file for more information.
#include "Res.h"
// You should remember these files from the previous demos
#include "SexyAppFramework/Graphics.h"
#include "SexyAppFramework/Color.h"
#include "SexyAppFramework/Rect.h"
#include "SexyAppFramework/ButtonWidget.h"
#include "SexyAppFramework/WidgetManager.h"
#include "SexyAppFramework/ImageFont.h"
#include "SexyAppFramework/Image.h"
// Our example dialog box
#include "DemoDialog.h"
// And for our performance profiling example, we first have to define SEXY_PERF_ENABLED
// before including PerfTimer.h:
#define SEXY_PERF_ENABLED
#include "SexyAppFramework/PerfTimer.h"
// Lastly, for our example of how to catch memory leaks, we first
// enable leak detection with a #define in EACH of the files we want
// to find leaks in, then include the memory management file.
// IT IS VERY IMPORTANT THAT THIS DEFINE AND INCLUDE BE DONE AFTER ALL
// OTHER FILE INCLUDES OR ELSE IT WILL CAUSE LINKER AND COMPILER ERRORS!
// Memory leaks will automatically be dumped to "mem_leaks.txt" when
// the app is closed.
#define SEXY_MEMTRACE
#include "SexyAppFramework/memmgr.h"
// The SexyAppFramework resides in the "Sexy" namespace. As a convenience,
// you'll see in all the .cpp files "using namespace Sexy" to avoid
// having to prefix everything with Sexy::
using namespace Sexy;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
Board::Board(GameApp* theApp)
{
mApp = theApp;
mButton = NULL;
// Set up our parallaxing layers
for (int i = 0; i < 3; i++)
{
mLayer[i].mX = 0;
// Get the image for this layer. You'll notice in our resource
// file that we named the layers IMAGE_BG0, IMAGE_BG1, IMAGE_BG2.
// Instead of having to manually type in the layer names for each one,
// we can use some convenience routines from our generated Res.h/.cpp
// resource files. First step: make a string of the ID of the image we want
// to access:
std::string imageStringID = StrFormat("IMAGE_BG%d", i);
// what we need to do now is get the integer ID for our resource
// that has the same name as imageStringID.
int id = GetIdByStringId(imageStringID.c_str());
// Now that we have the integer ID, we can request the actual
// image data with it. We do that with GetImageById:
mLayer[i].mImage = GetImageById(id);
// Set the Y coordinate of the background layer so that it's
// base is at the bottom of the screen. Because the Board hasn't
// been resized or added to the manager yet, it's own mHeight and
// mWidth are at the default of 0, 0. But not to worry, we set the
// overall game's width/height in GameApp's constructor, so we
// can just use those variables instead:
mLayer[i].mY = (float)(mApp->mHeight - mLayer[i].mImage->GetHeight());
}
// We will ON PURPOSE leak memory as an example of how to track
// such things in your program. Review the comments at the top of this
// file about memory leak detection:
int* aLeakedInteger = new int;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
Board::~Board()
{
delete mButton;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::Update()
{
// As an example of how to profile things, we're going to profile
// the update and draw functions. Clearly, profiling this almost
// empty function isn't too useful, but the point is to show you
// HOW to profile multiple things and how to view the results.
// You tell the profiler that you want it to begin with the
// statement below, passing in a string indicating WHAT you're
// profiling. The string can be anything. Enabled debug keys by
// pressing CTRL-ALT-D and then press F2 to enabled/disable profiling.
SEXY_PERF_BEGIN("Start_Of_Update");
Widget::Update();
MarkDirty();
// And you mark the end of a profiling section with
// SEXY_PER_END, passing in the same string you passed to
// SEXY_PERF_BEGIN.
SEXY_PERF_END("Start_Of_Update");
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::KeyDown(KeyCode theKey)
{
// If the user presses the right or left arrow keys, let's
// scroll the layers:
if (theKey == KEYCODE_RIGHT)
{
for (int i = 0; i < 3; i++)
{
// When the X coordinate has moved leftward (negative) by
// the image's width or more, we reset the X coordinate back to
// 0. Since we tile an image to the left and right of
// this center image, the user won't see the snapping back to 0.
mLayer[i].mX -= 1.0f * (i + 1);
if (mLayer[i].mX <= -mLayer[i].mImage->GetWidth())
mLayer[i].mX = 0;
}
}
else if (theKey == KEYCODE_LEFT)
{
for (int i = 0; i < 3; i++)
{
// This is the opposite of above, but notice that we're checking
// to see if the X coordinate is >= the width of the image, and not the
// width of the application. That's because some of the layers are wider
// than the screen: 960 wide, when the game is 640 wide. If we checked
// against the game width instead, we'd get this odd flicker appearing
// because the tiling would be off. You can try it and see the effect.
mLayer[i].mX += 1.0f * (i + 1);
if (mLayer[i].mX >= mLayer[i].mImage->GetWidth())
mLayer[i].mX = 0;
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::Draw(Graphics* g)
{
// As an example of how to profile things, we're going to profile
// the update and draw functions.
// You tell the profiler that you want it to begin with the
// statement below, passing in a string indicating WHAT you're
// profiling. The string can be anything. Enabled debug keys by
// pressing CTRL-ALT-D and then press F2 to enabled/disable profiling.
SEXY_PERF_BEGIN("Start_Of_Draw");
// Clear the screen to black
g->SetColor(Color(0, 0, 0));
g->FillRect(0, 0, mWidth, mHeight);
for (int i = 0; i < 3; i++)
{
int imgWidth = mLayer[i].mImage->GetWidth();
// Let's learn about Graphics translation. Normally, you draw your image
// at a given XY by passing the coordinates to DrawImage. As an alternative,
// you can "translate" the Graphics object (change its XY drawing offset)
// and then just draw your image at 0, 0. Think of the Graphics object by
// default as always being at 0, 0. By translating it, you move that
// to whereever you like. The usefulness is in saving extra typing,
// as well as some other features that come into play when scrolling
// large worlds with many objects. For each of the background layers,
// let's move the graphics object to its base XY location:
g->TranslateF(mLayer[i].mX, mLayer[i].mY);
// Something else to know about translation is that it is the method
// used when drawing widgets. For every widget, the Graphics object
// is translated to its own X, Y coordinate. Thus, when drawing
// in the widget's class, you should always draw as if the top left
// corner of the widget itself was 0, 0. Since all along we've been making
// our Board widget the size of the screen, this hasn't been something we've
// had to care about. But as you'll see in the dialog box example, it's something
// we have to take into consideration. Normally, you can't draw outside the
// X, Y, width, height of a widget. However, as you'll also see, you can
// change that to allow drawing anywhere you want.
// Remember how in previous demos we mentioned that it's common to use the
// smoother DrawImageF functions instead of DrawImage if the user has 3D
// acceleration enabled? Let's do that know. We can check if hardware acceleration
// is on or not with a call to SexyAppBase's Is3DAccelerated:
if (mApp->Is3DAccelerated())
{
// Because we translated the graphics object, we can just draw our layers
// offset by the image width. Had we not translated the graphics object, here
// is the code we would have had to use instead:
// g->DrawImageF(mLayer[i].mImage, mLayer[i].mX - imgWidth, mLayer[i].mY);
// g->DrawImageF(mLayer[i].mImage, mLayer[i].mX, mLayer[i].mY);
// g->DrawImageF(mLayer[i].mImage, mLayer[i].mX + imgWidth, mLayer[i].mY);
g->DrawImageF(mLayer[i].mImage, (float)-imgWidth, 0.0f);
g->DrawImageF(mLayer[i].mImage, 0.0f, 0.0f);
g->DrawImageF(mLayer[i].mImage, (float)imgWidth, 0.0f);
}
else
{
// non-hardware accelerated
g->DrawImage(mLayer[i].mImage, -imgWidth, 0);
g->DrawImage(mLayer[i].mImage, 0, 0);
g->DrawImage(mLayer[i].mImage, imgWidth, 0);
}
// You should remember to put the Graphics object back to where it was
// before you translated it, otherwise all subsequent drawing commands will
// be offset by that amount.
g->TranslateF(-mLayer[i].mX, -mLayer[i].mY);
}
// And you mark the end of a profiling section with
// SEXY_PER_END, passing in the same string you passed to
// SEXY_PERF_BEGIN.
SEXY_PERF_END("Start_Of_Draw");
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::AddedToManager(WidgetManager* theWidgetManager)
{
// At this point, the Board class has already been added to the
// widget manager. We should call our parent class' method
// so that it can be sure to perform any needed tasks, first.
Widget::AddedToManager(theWidgetManager);
// You should remember how to create buttons from Demo3. If not,
// go back and review.
mButton = new ButtonWidget(Board::OPTIONS_BUTTON_ID, this);
mButton->SetFont(FONT_DEFAULT);
mButton->mLabel = _S("Click Me!");
// This time, let's use some images for our button.
// mOverImage is the image to use when the mouse cursor is over the button.
// mDownImage is the image to use when a mouse button is held down on it
// mButtonImage is the default image to use
// If we wanted to, we could specify a disabled image as well.
mButton->mOverImage = IMAGE_BUTTON_OVER;
mButton->mDownImage = IMAGE_BUTTON_DOWN;
mButton->mButtonImage = IMAGE_BUTTON_NORMAL;
mButton->mDoFinger = true;
mButton->Resize(56, 5, IMAGE_BUTTON_NORMAL->GetWidth(), IMAGE_BUTTON_NORMAL->GetHeight());
theWidgetManager->AddWidget(mButton);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::RemovedFromManager(WidgetManager* theWidgetManager)
{
// This is called after we've been removed from the widget manager.
// Again, we should let our base class do anything it needs to, first.
Widget::RemovedFromManager(theWidgetManager);
// We should now also remove any widgets we are responsible for.
theWidgetManager->RemoveWidget(mButton);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::ButtonDepress(int theId)
{
// As another PURPOSEFUL example of detecting memory leaks, let's
// cause a leak here as well so you can see how the leak detection works.
// Again, this is on purpose to illustrate a point.
struct ParallaxLayer* aLeakedParallaxLayer = new struct ParallaxLayer;
if (theId == Board::OPTIONS_BUTTON_ID)
{
// Let's open our options dialog box. Everything works the same
// except that instead of using the WidgetManager's AddWidget function,
// we use a special AddDialog method. The first parameter is the ID
// of the dialog box, the second is the dialog box itself.
// In this case, we don't need to maintain a pointer to the dialog box
// to delete later. It will automatically be cleaned up when the
// dialog box is closed via the KillDialog command (see DemoDialog).
DemoDialog* dlg = new DemoDialog("Header", "Hello! I am a dialog box.");
dlg->Resize(50, 50, 300, 400);
mApp->AddDialog(DemoDialog::DIALOG_ID, dlg);
}
}