//board.h
#ifndef __BOARD_H__
#define __BOARD_H__
#include "SexyAppFramework/Widget.h"
#include "SexyAppFramework/ButtonListener.h"
// 本例子我们将学习几个新的部件: 输入框. 单选框. 列表框. 所以要用到这些部件的事件监听器类.
#include "SexyAppFramework/EditListener.h"
#include "SexyAppFramework/CheckboxListener.h"
#include "SexyAppFramework/ListListener.h"
class Graphics;
class GameApp;
// 声明几个将要使用的部件类
class ButtonWidget;
class EditWidget;
class Checkbox;
class ListWidget;
class ScrollbarWidget;
class WidgetManager;
class Board : public Widget, public ButtonListener,
public EditListener, public CheckboxListener,
public ListListener
{
GameApp* mApp;
ButtonWidget* mButton1;
ButtonWidget* mButton2;
EditWidget* mEditWidget;
Checkbox* mCheckboxWidget;
ListWidget* mListWidget;
ScrollbarWidget* mScrollbarWidget;
SexyString mText;
//两个运动图片的X坐标.
float mMotionX;
float mUpdateFMotionX;
public:
Board(GameApp* theApp);
virtual ~Board();
//
// 从输入框事件监听器的函数
// 在编辑框输入时按下 回车 键会自动调用该消息.
void EditWidgetText(int theId, const std::string& theString);
//
// 从编辑框事件监听器继承的函数
// 当有任何 字母 被输入时会自动调用该函数.
// 返回值为 true 表示输入的字符被加入到原来的字符串. 返回false表示不加入.
bool AllowChar(int theId, char theChar);
//
// 从单选框事件监听器继承的函数
// 当选择/取消事件发生时会自动调用该函数.
void CheckboxChecked(int theId, bool checked) {;}
//
// 从列表框事件监听器继承的函数.
// 当点击列表框中的子项时会自动调用该函数.
// 参数为 列表框ID, 选择第几个子项, 鼠标的哪个键在点击
void ListClicked(int theId, int theIdx, int theClickCount);
virtual void Draw(Graphics* g);
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);
virtual void ButtonDepress(int theId);
virtual void AddedToManager(WidgetManager* theWidgetManager);
virtual void RemovedFromManager(WidgetManager* theWidgetManager);
};
#endif // __BOARD_H__
//gameapp.h
#ifndef __GAMEAPP_H__
#define __GAMEAPP_H__
#include "SexyAppFramework/SexyAppBase.h"
class Board;
// 为游戏增加前导屏幕而定义的类
class TitleScreen;
class GameApp : public SexyAppBase
{
Board* mBoard;
TitleScreen* mTitleScreen;
public:
GameApp();
virtual ~GameApp();
virtual void Init();
virtual void LoadingThreadProc();
virtual void LoadingThreadCompleted();
//
// 在前导屏幕结束时被 TitleScreen 类调用. 然后撤销掉前导屏幕部件. 并加载Board部件.
void TitleScreenIsFinished();
//
// 检查主函数的参数
virtual void HandleCmdLineParam(const std::string& theParamName, const std::string& theParamValue);
};
#endif // __GAMEAPP_H__
//res.h
#ifndef __Res_H__
#define __Res_H__
namespace Sexy
{
class ResourceManager;
class Image;
class Font;
Image* LoadImageById(ResourceManager *theManager, int theId);
bool ExtractResourcesByName(ResourceManager *theManager, const char *theName);
// Game Resources
bool ExtractGameResources(ResourceManager *theMgr);
extern Image* IMAGE_BG0;
extern Image* IMAGE_BG1;
extern Image* IMAGE_BG2;
extern Image* IMAGE_BUTTON_DOWN;
extern Image* IMAGE_BUTTON_NORMAL;
extern Image* IMAGE_BUTTON_OVER;
extern Image* IMAGE_CHECKBOX;
extern Image* IMAGE_DIALOG_BOX;
extern Image* IMAGE_DIALOG_BUTTON;
extern Image* IMAGE_ROBOTROBOT;
extern Image* IMAGE_SLIDER_THUMB;
extern Image* IMAGE_SLIDER_TRACK;
extern int SOUND_MUTATOR;
extern int SOUND_TIMER;
// Hungarr Resources
bool ExtractHungarrResources(ResourceManager *theMgr);
extern Image* IMAGE_ATOMIC_EXPLOSION;
extern Image* IMAGE_BOMB_RADIAL_DEATH;
extern Image* IMAGE_HUNGARR_BEAM_DOWN;
extern Image* IMAGE_HUNGARR_BEAM_LEFT;
extern Image* IMAGE_HUNGARR_BEAM_RIGHT;
extern Image* IMAGE_HUNGARR_BEAM_UP;
extern Image* IMAGE_HUNGARR_HORIZ;
extern Image* IMAGE_HUNGARR_SMALL;
extern Image* IMAGE_HUNGARR_VERT;
extern Image* IMAGE_PARTICLE_LIGHTNING;
extern Image* IMAGE_PLANETS;
extern Image* IMAGE_SPARK;
extern int SOUND_BEAM_HIT;
extern int SOUND_BEAM_MOVING;
extern int SOUND_BUTTON;
extern int SOUND_EXPLOSION;
extern int SOUND_GAME_OVER_CLICK;
extern int SOUND_GAME_OVER_RESTART;
extern int SOUND_GAME_OVER_STATS;
extern int SOUND_GAME_OVER_TEXT;
extern int SOUND_LEVEL_UP1;
extern int SOUND_LEVEL_UP2;
extern int SOUND_LEVEL_UP3;
extern int SOUND_LEVEL_UP4;
extern int SOUND_MAGZAP;
extern int SOUND_PLANET;
extern int SOUND_PLANET_HIT;
extern int SOUND_REGION_FILLED;
// Init Resources
bool ExtractInitResources(ResourceManager *theMgr);
extern Font* FONT_DEFAULT;
extern Font* FONT_HUNGARR;
extern Image* IMAGE_CUSTOM_DRAGGING;
extern Image* IMAGE_CUSTOM_HAND;
extern Image* IMAGE_CUSTOM_POINTER;
extern Image* IMAGE_CUSTOM_TEXT;
extern Image* IMAGE_HUNGARR_LOGO;
// TitleScreen Resources
bool ExtractTitleScreenResources(ResourceManager *theMgr);
extern Image* IMAGE_LOADER_BAR;
extern Image* IMAGE_LOADER_LOADINGTXT;
extern int SOUND_CONTINUE;
enum ResourceId
{
FONT_DEFAULT_ID,
FONT_HUNGARR_ID,
IMAGE_CUSTOM_POINTER_ID,
IMAGE_CUSTOM_HAND_ID,
IMAGE_CUSTOM_DRAGGING_ID,
IMAGE_CUSTOM_TEXT_ID,
IMAGE_HUNGARR_LOGO_ID,
IMAGE_LOADER_BAR_ID,
IMAGE_LOADER_LOADINGTXT_ID,
SOUND_CONTINUE_ID,
SOUND_MUTATOR_ID,
SOUND_TIMER_ID,
IMAGE_ROBOTROBOT_ID,
IMAGE_CHECKBOX_ID,
IMAGE_BG0_ID,
IMAGE_BG1_ID,
IMAGE_BG2_ID,
IMAGE_BUTTON_DOWN_ID,
IMAGE_BUTTON_OVER_ID,
IMAGE_BUTTON_NORMAL_ID,
IMAGE_DIALOG_BOX_ID,
IMAGE_DIALOG_BUTTON_ID,
IMAGE_SLIDER_TRACK_ID,
IMAGE_SLIDER_THUMB_ID,
IMAGE_HUNGARR_SMALL_ID,
IMAGE_HUNGARR_BEAM_UP_ID,
IMAGE_HUNGARR_BEAM_DOWN_ID,
IMAGE_HUNGARR_BEAM_LEFT_ID,
IMAGE_HUNGARR_BEAM_RIGHT_ID,
IMAGE_HUNGARR_HORIZ_ID,
IMAGE_HUNGARR_VERT_ID,
IMAGE_ATOMIC_EXPLOSION_ID,
IMAGE_BOMB_RADIAL_DEATH_ID,
IMAGE_PLANETS_ID,
IMAGE_SPARK_ID,
IMAGE_PARTICLE_LIGHTNING_ID,
SOUND_MAGZAP_ID,
SOUND_BUTTON_ID,
SOUND_PLANET_ID,
SOUND_LEVEL_UP1_ID,
SOUND_LEVEL_UP2_ID,
SOUND_EXPLOSION_ID,
SOUND_BEAM_HIT_ID,
SOUND_PLANET_HIT_ID,
SOUND_BEAM_MOVING_ID,
SOUND_LEVEL_UP4_ID,
SOUND_LEVEL_UP3_ID,
SOUND_GAME_OVER_CLICK_ID,
SOUND_GAME_OVER_STATS_ID,
SOUND_GAME_OVER_RESTART_ID,
SOUND_GAME_OVER_TEXT_ID,
SOUND_REGION_FILLED_ID,
RESOURCE_ID_MAX
};
Image* GetImageById(int theId);
Font* GetFontById(int theId);
int GetSoundById(int theId);
ResourceId GetIdByImage(Image *theImage);
ResourceId GetIdByFont(Font *theFont);
ResourceId GetIdBySound(int theSound);
const char* GetStringIdById(int theId);
ResourceId GetIdByStringId(const char *theStringId);
} // namespace Sexy
#endif
//titleScreen.h
#ifndef __TITLE_SCREEN_H__
#define __TITLE_SCREEN_H__
#include "SexyAppFramework/Widget.h"
#include "SexyAppFramework/ButtonListener.h"
namespace Sexy
{
class GameApp;
class Graphics;
class WidgetManager;
// A new widget that we'll be learning about. It's explained in the .CPP code.
class HyperlinkWidget;
// 超连接类和按钮类都是用 ButtonListener 作为监听器.
class TitleScreen : public Widget, public ButtonListener
{
GameApp* mApp;
HyperlinkWidget* mContinueLink;
public:
TitleScreen(GameApp* pApp){
mApp = pApp;
mContinueLink = NULL;
}
virtual ~TitleScreen(){ delete mContinueLink;}
void Init();
void AddedToManager(WidgetManager* theWidgetManager);
void RemovedFromManager(WidgetManager* theWidgetManager);
virtual void ButtonDepress(int theId);
void Draw(Graphics* g);
//
// 在 GameApp::LoadingThreadCompleted() 中手动调用.
// 用来告诉该前导屏幕部件 资源已经加载完毕.
void LoadingComplete();
};
}
#endif //__TITLE_SCREEN_H__
//board.cpp
#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"
#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/EditWidget.h"
#include "SexyAppFramework/Checkbox.h"
#include "SexyAppFramework/ListWidget.h"
#include "SexyAppFramework/ScrollbarWidget.h"
#include "SexyAppFramework/ScrollListener.h"
// 处理声音
#include "SexyAppFramework/SoundManager.h"
#include "SexyAppFramework/SoundInstance.h"
// 处理文件I/O
#include "SexyAppFramework/Buffer.h"
using namespace Sexy;
Board::Board(GameApp* theApp)
{
mApp = theApp;
mButton1 = NULL;
mButton2 = NULL;
mEditWidget = NULL;
mCheckboxWidget = NULL;
mListWidget = NULL;
mScrollbarWidget = NULL;
mMotionX = mUpdateFMotionX = 0;
}
Board::~Board()
{
delete mButton1;
delete mButton2;
delete mEditWidget;
delete mCheckboxWidget;
delete mListWidget;
delete mScrollbarWidget;
}
//为了说明平滑的运动(使用 UpdateF) 和 普通的运动(使用 Update).
//这个例子中同时使用了2者. 可以对比它们的效果.
//
void Board::Update()
{
Widget::Update();
// 普通的运动. 每次移动 5 象素.
// 但因为游戏的帧频率和屏幕的刷新率不一样. 可能造成 这次移动了4象素. 下次又移动6象素. 移动效果不稳定.
if ((mMotionX += 5.0f) >= mWidth)
mMotionX = (float) -IMAGE_ROBOTROBOT->GetWidth();
MarkDirty();
}
//
// 这个函数的详细说明在 Board.h 中.
void Board::UpdateF(float theFrac)
{
// 参数是一个比例. 表示屏幕的实际刷新率 与 游戏100Hz 的帧频率 的比值.
// 这样每次屏幕刷新时可以计算出 移动物体此时的坐标.
if ((mUpdateFMotionX += 5.0f * theFrac) >= mWidth)
mUpdateFMotionX = (float) -IMAGE_ROBOTROBOT->GetWidth();
// 不用再 MarkDirty (因为Update()已经做过了).
}
//
//
void Board::Draw(Graphics* g)
{
g->SetColor(Color(0, 0, 0));
g->FillRect(0, 0, mWidth, mHeight);
// Draw the image first using the standard method, with the coordinate
// updated via Board::Update as an example of non-smooth motion.
// Let's also print some text too.
g->SetFont(FONT_DEFAULT);
g->SetColor(Color(255, 255, 255));
g->DrawString(_S("Non smooth motion is jerky"), 10, 100);
// What's this? A new DrawImage function? Yes. Believe it. It is true.
// DrawImageF is just like it's best friend, DrawImage except that
// it takes floating point values instead of integer ones. This is
// slower than DrawImage, as the resulting image is anti-aliased to
// give the illusion of moving at sub-pixel increments. A common
// optimization technique at PopCap is to use DrawImageF
// for motion when the user has a supported 3D card and DrawImage if
// the user has to run in software mode.
g->DrawImageF(IMAGE_ROBOTROBOT, mMotionX, 120.0f);
// Now let's draw the image but using the smooth motion amounts:
g->DrawString(_S("Smooth motion is silky smoothness"), 10, 200);
g->DrawImageF(IMAGE_ROBOTROBOT, mUpdateFMotionX, 220.0f);
// Let's draw the currently selected list item:
g->DrawString(mText, mListWidget->mX, mListWidget->mY + mListWidget->mHeight + 20);
}
//
//
void Board::AddedToManager(WidgetManager* theWidgetManager)
{
Widget::AddedToManager(theWidgetManager);
// 两个播放声音的按钮
mButton1 = new ButtonWidget(1, this);
mButton1->Resize(5, 5, 100, 50);
mButton1->SetFont(FONT_DEFAULT);
mButton1->mLabel = _S("Sound Left");
theWidgetManager->AddWidget(mButton1);
mButton2 = new ButtonWidget(2, this);
mButton2->Resize(106, 5, 100, 50);
mButton2->SetFont(FONT_DEFAULT);
mButton2->mLabel = _S("Sound Right");
theWidgetManager->AddWidget(mButton2);
// 创建输入框部件.
mEditWidget = new EditWidget(1, this);
mEditWidget->SetFont(FONT_DEFAULT); //必须设置输入框的字体. 否则无法输入.
mEditWidget->mMaxChars = 15; // 最多字符数
// 如果是输入密码. 可以设置 mEditWidget->mPasswordChar 成员
mEditWidget->Resize(10, 300, 100, 15);
theWidgetManager->AddWidget(mEditWidget);
// 创建单选框.
// 单选框没有缺省的绘制函数. 所以它和其他的部件不同.
// 我们必须在创建时指定两幅图片表示它的选择和不选. 也可以把这两个图像放入一个图片文件. 如下例:
mCheckboxWidget = new Checkbox(IMAGE_CHECKBOX, IMAGE_CHECKBOX, 1, this);
// 然后必须告诉单选框. 在这唯一的一副图片中. 哪部分是"选择"用的. 哪部分是"不选"用的.
int checkWidth = IMAGE_CHECKBOX->GetWidth() / 2;
mCheckboxWidget->mUncheckedRect = Rect(0, 0, checkWidth, IMAGE_CHECKBOX->GetHeight());
mCheckboxWidget->mCheckedRect = Rect(checkWidth, 0, checkWidth, IMAGE_CHECKBOX->GetHeight());
mCheckboxWidget->Resize(200, 300, checkWidth, IMAGE_CHECKBOX->GetHeight());
theWidgetManager->AddWidget(mCheckboxWidget);
// 创建列表框
// 参数为 ID, 字体, 监听器
mListWidget = new ListWidget(1, FONT_DEFAULT, this);
// 是否显示边框
mListWidget->mDrawOutline = true;
// 设置列表框的颜色. 先用一个颜色数组准备一些颜色. 再用 SetColors() 设置之.
int listWidgetColors[][3] =
{
{255, 255, 255}, //背景色
{255, 0, 0}, //边框色
{0, 0, 0}, //文本色(未选择时)
{0, 0, 255}, //文本色(鼠标经过)
{128, 128, 128}, //条目被选择后的颜色
{190, 0, 80} //文本色(被选择后).
};
mListWidget->SetColors(listWidgetColors, 6);
// 为上一步的列表框创建一个滚动条
mScrollbarWidget = new ScrollbarWidget(1, mListWidget);
mListWidget->mScrollbar = mScrollbarWidget;
// 要在滚动条设置之后. 列表框再 Resize().
mListWidget->Resize(300, 300, 100, 100);
// 将滚动条移动到 列表框右边.
mScrollbarWidget->ResizeScrollbar(mListWidget->mX + mListWidget->mWidth,
mListWidget->mY,
25, // an arbitrary width for the bar itself
mListWidget->mHeight);
// 隐藏滚动条. 直到列表框已满.
mScrollbarWidget->SetInvisIfNoScroll(true);
theWidgetManager->AddWidget(mListWidget);
theWidgetManager->AddWidget(mScrollbarWidget);
// If you check out Board::EditWidgetText you'll see that we write out the
// contents of the list box to disk every time something is added to it, or
// it is cleared. For files that were created from a buffer, we can read
// the data back into a buffer for ease of use.
//
// IMPORTANT: You can NOT load files into a buffer if they were not
// created from a buffer in the first place. Thus, loading a random
// text or binary file is not a good idea, since the buffer will
// have no way to identify the datatypes beyond just a standard byte.
// Plase read Board::EditWidgetText for an explanation of writing
// with buffers.
//
// Let's load in the contents of that file, if it exists,
// and repopulate the list box with it. The first step is to create
// a Buffer object. The Buffer object will contain all the data
// in the file:
Buffer buffer;
// Check if the file exists:
if (mApp->FileExists("list_items.dat"))
{
// Read in all the data from the file. It will be stored in a format
// unique to the Buffer object:
if (!mApp->ReadBufferFromFile("list_items.dat", &buffer))
{
// error, the file was corrupted or some other error occurred
mApp->Popup("Could not read contents of list_items.txt");
}
// In Board::EditWidgetText we wrote out all the strings
// via buffer.WriteString. The order in which data is written
// to the buffer is the order in which it is read. Thus, if
// you wrote out 2 integers, a string, and 10 booleans, when
// reading from the buffer you'd first ask for the 2 integers,
// then the string, then the 10 booleans. This is important:
// the order DOES matter. If you asked to read a string when
// a long was actually the next element in the file, you'd
// get an error. Buffers are very useful for userprofiles or
// data files like that where you can guarantee an explicit
// file format.
while (!buffer.AtEnd())
mListWidget->AddLine(StringToSexyStringFast(buffer.ReadString()), true);
}
// If you read Board::ListClicked, you'll see that we
// wrote the last selected item from the list box to the registry.
// Let's read that value in, if it exists, and set the mText
// variable to it so that it displays on screen. We use the
// RegsitryRead... functions to grab values from the regsitry.
// The values are assumed to be in the registry location that you
// set by setting the mRegKey variable in GameApp's constructor.
// The functions return false if there was an error reading the
// key or the key doesn't exist:
mApp->RegistryReadString("ListItem", &SexyStringToStringFast(mText));
}
//
void Board::RemovedFromManager(WidgetManager* theWidgetManager)
{
Widget::RemovedFromManager(theWidgetManager);
theWidgetManager->RemoveWidget(mButton1);
theWidgetManager->RemoveWidget(mButton2);
theWidgetManager->RemoveWidget(mEditWidget);
theWidgetManager->RemoveWidget(mCheckboxWidget);
theWidgetManager->RemoveWidget(mListWidget);
theWidgetManager->RemoveWidget(mScrollbarWidget);
}
//
void Board::ButtonDepress(int theId)
{
if (theId == 1)
{
// Our "left" button was clicked. Let's play a sound
// in the left speaker with a slight pitch shift.
// In order to play a pitch shifted sample, we have to do more
// than just say "PlaySample." We have to get a pointer to the sound
// instance that represents our sound effect. We do that by asking the
// app's sound manager to return us a sound instance, and we tell it
// the ID of the sound file that we want. Let's do that now,
// using the sound "SOUND_MUTATOR" which we set up in properties/resources.xml:
SoundInstance* sample = mApp->mSoundManager->GetSoundInstance(SOUND_MUTATOR);
// It's good to make sure the sample isn't NULL. It would be NULL if you
// specified an invalid sound id.
if (sample != NULL)
{
//Now we actually adjust the pitch. Specify the number of
//steps to raise (positive) or lower (negative) the original sound by.
//We'll just arbitrarily choose 13.
sample->AdjustPitch(13);
//Let's make it play on the left speaker only. We set a panning value
//in decibels, which for DirectX range from -10000 to +10000, where
//-10000 is fully left and +10000 is fully right:
sample->SetPan(-10000);
// Now we have the sample play. This is again slightly different than
// our PlaySample from previous demos. The first parameter indicates
// whether or not we want the sample to loop (in this case, no), and
// the second indicates whether the memory taken up by this special
// sound instance pointer should be reclaimed when the sample is done
// playing. If true, then the pointer will be invalid once its
// done playing, sine the memory will be reclaimed. If false, the
// memory won't be reclaimed and you'll have to do it yourself.
sample->Play(false, true);
}
}
else if (theId == 2)
{
// Let's do the same as we did for the left button, except make it
// play on the right speaker and pitch shift it down
SoundInstance* sample = mApp->mSoundManager->GetSoundInstance(SOUND_MUTATOR);
if (sample != NULL)
{
sample->AdjustPitch(-5);
sample->SetPan(10000);
sample->Play(false, true);
}
}
}
//
// 当在输入框输入 回车 时. 会调用该函数.
void Board::EditWidgetText(int theId, const std::string& theString)
{
if (theString.length() > 0)
{
// 当输入框字符串为 "clear" 时. 清空列表框. 否则将输入的字符串加入列表框.
if (StringToUpper(theString) == "CLEAR")
mListWidget->RemoveAll();
else
mListWidget->AddLine(StringToSexyStringFast(theString), true);
mEditWidget->SetText(_S(""));
// 将列表框中的所有字符串写入 buffer.
// 再将buffer写入文件.
Buffer buffer;
for (unsigned int i = 0; i < mListWidget->mLines.size(); i++)
buffer.WriteString(SexyStringToStringFast(mListWidget->mLines.at(i)));
mApp->WriteBufferToFile("list_items.dat", &buffer);
}
}
//
//
bool Board::AllowChar(int theId, char theChar)
{
// As an example of denying input, let's prevent the user
// from typing in the following: :-+.@#$%^&*()
switch (theChar)
{
case ':':
case '-':
case '+':
case '.':
case '@':
case '#':
case '$':
case '%':
case '^':
case '&':
case '*':
case '(':
case ')':
return false;
default:
return true;
}
}
//
//
void Board::ListClicked(int theId, int theIdx, int theClickCount)
{
if (theId == 1)
{
// Actually select the index. This is done to allow us to
// block the selection, if we chose to.
mListWidget->SetSelect(theIdx);
// And update the text that's displaying on screen...
// The strings are stored in the list widget's mLines variable
// and the one clicked can be indexed via theIdx.
mText = mListWidget->mLines[theIdx];
// As an example of writing to the registry, let's write this value
// and later on load it in when we restart the app. You'll notice
// a bunch of RegistryWrite... functions in SexyAppBase. Each one
// of these takes as first argument the name of the value to
// add to the registry. The second argument is the actual data
// to store in that value. The value is saved under the registry
// location that you set in GameApp's constructor when you
// set the mRegKey variable. The function returns false
// if there was an error, such as a lack of permission:
if (!mApp->RegistryWriteString("ListItem", SexyStringToStringFast(mText)))
mApp->Popup("Couldn't save \"ListItem\" to registry");
}
}
//gameapp.cpp
#include "GameApp.h"
#include "TitleScreen.h"
#include "Board.h"
#include "SexyAppFramework/WidgetManager.h"
// 这个例子将使用资源管理器类
#include "SexyAppFramework/ResourceManager.h"
// 声音
#include "SexyAppFramework/BassMusicInterface.h"
// 从 resources.xml 文件加载资源
#include "Res.h"
using namespace Sexy;
//
GameApp::GameApp()
{
mProdName = "Demo 4";
mProductVersion = "1.0";
mTitle = StringToSexyStringFast("SexyAppFramework: " + mProdName + " - " + mProductVersion);
mWidth = 800;
mHeight = 600;
mAutoEnable3D = true;
mBoard = NULL;
mTitleScreen = NULL;
// See Board::UpdateF for a very lengthy explanation of this and smooth motion
mVSyncUpdates = true;
}
//
GameApp::~GameApp()
{
if (mBoard != NULL)
mWidgetManager->RemoveWidget(mBoard);
delete mBoard;
//如果在前导屏幕结束前关闭游戏. 需要手动将前导部件remove
if (mTitleScreen != NULL)
mWidgetManager->RemoveWidget(mTitleScreen);
delete mTitleScreen;
// 总是应该释放所有加载的资源组. 因为对已经释放的资源组再释放一遍也没事.
mResourceManager->DeleteResources("Init");
mResourceManager->DeleteResources("TitleScreen");
mResourceManager->DeleteResources("Game");
}
//
void GameApp::Init()
{
SexyAppBase::Init();
// 首先. 告诉资源管理器去读取所有的组.
// 读取的资源清单默认在"properties/resources.xml".
// 但这个操作并不加载任何资源. 它只是解析xml中的数据.
LoadResourceManifest();
// 接着. 通过资源管理器真正的加载资源.
// 这只要调用 mResourceManager->LoadResources() 并指定组名. 这里组名是"Init".
if (!mResourceManager->LoadResources("Init"))
{
mLoadingFailed = true;
// 将显示错误信息. 来报告在加载线程中发生了什么错误
ShowResourceError(true);
return;
}
// 转换加载的资源.
// 例如把加载的声音转换为wav文件. 对images做palletizes等.
if (!ExtractInitResources(mResourceManager))
{
mLoadingFailed = true;
ShowResourceError(true);
return;
}
// We also need to load our title screen graphics in, since you can't
// display the title screen without any graphics. For an explanation of why
// we placed this in a separate group from Init, see properties/resources.xml.
// This code works exactly like the above did for the Init group.
// 加载 TitleScreen 组中的资源.
if (!mResourceManager->LoadResources("TitleScreen"))
{
mLoadingFailed = true;
ShowResourceError(true);
return;
}
if (!ExtractTitleScreenResources(mResourceManager))
{
mLoadingFailed = true;
ShowResourceError(true);
return;
}
// 创建前导屏幕部件. 并将其加入部件管理器
mTitleScreen = new TitleScreen(this);
mTitleScreen->Resize(0, 0, mWidth, mHeight);
mTitleScreen->Init();
mWidgetManager->AddWidget(mTitleScreen);
// 载入声音.
// 所有与声音有关的东西. 包括载入. 播放. 停止. 音量等等..
// 我们都用 APP 的 mMusicInterface 成员搞定.
mMusicInterface->LoadMusic(0, "music/music.mo3");
mMusicInterface->LoadMusic(1, "music/music.mo3");
// 播放声音用 MusicInterface::PlayMusic(id, offset, no_loop);
// 要播放时有淡出淡入效果用 MusicInterface::FadeIn(id, offset, speed, no_loop);
// 参数为:
// id . 音频ID 加载时指定.
// offset. 不知何物.
// speed. 淡出淡入的速度. 貌似在 0.001左右时有效.
// no_loop. 是否"不循环". 注意: false 表示循环.
mMusicInterface->FadeIn(0, 0, 0.002, false);
// 这时我们要知道有多少资源需要加载. 因为前导屏幕的进度条将使用它.
// SexyAppBase::mNumLoadingThreadTasks 成员用来保存该值. 所以下边设置它.
// //通过资源管理器的GetNumResources(组名)函数可以得到改组的资源数.
mNumLoadingThreadTasks = mResourceManager->GetNumResources("Game");
}
//
void GameApp::LoadingThreadProc()
{
// 这次我们不再手工加载资源. 而是交给 resource manager 处理.
// 给出组名调用 StartLoadResources() 开始加载. 把该组做为当前组.
mResourceManager->StartLoadResources("Game");
// 加载每个个体资源.
// LoadNextResource() 加载当前组中的下一个资源. 若没有下一个. 返回false.
while (mResourceManager->LoadNextResource())
{
// App基类的又一个成员. 表示目前加载了多少个资源. 它也是用来在前导屏幕的进度条中使用.
// 我们每加载一个资源就手动修改该值.
mCompletedLoadingThreadTasks++;
// 如果资源加载器在加载资源时失败. 会设置下边的值. 我们应该检查它.
if (mShutdown)
return;
// 使前导屏幕变脏 . 导致进度条被重绘.
// 把它放在这里而不是 TitleScreen::Update() 中. 可以节约cpu时间.
mTitleScreen->MarkDirty();
}
// 和在Init()中做的一样. 加载资源后我们要将其转换. ExtractGameResources().
// 同时也检查错误.
if (mResourceManager->HadError() || !ExtractGameResources(mResourceManager))
{
ShowResourceError(false);
mLoadingFailed = true;
return;
}
}
//
//
void GameApp::LoadingThreadCompleted()
{
SexyAppBase::LoadingThreadCompleted();
if (mLoadingFailed)
return;
// 告诉前导屏幕. 资源已经加载完毕.
mTitleScreen->LoadingComplete(); //然后前导屏幕会显示出它的超连接.
mTitleScreen->MarkDirty();
}
//
// 在 TitleScreen::ButtonDepress() 函数中(即点击超连接后) 调用了该函数.
void GameApp::TitleScreenIsFinished()
{
mTitleScreen = NULL;
mBoard = new Board(this); // 撤销掉前导屏幕部件. 同时创建 Board 部件.
// 前导屏幕使用的资源这时可以释放掉了.
mResourceManager->DeleteResources("TitleScreen"); //释放一组资源.
mBoard->Resize(0, 0, mWidth, mHeight);
mWidgetManager->AddWidget(mBoard);
// 停止前导屏幕时的声音. 并开始播放下一个声音.
// 和 FadeIn() 类似. 这里是"淡出".
mMusicInterface->FadeOut(0, true, 0.004);
mMusicInterface->FadeIn(1, 9, 0.002, false);
// We'll cover changing the music and sound volumes in a later demo.
}
//
void GameApp::HandleCmdLineParam(const std::string& theParamName, const std::string& theParamValue)
{
//在这个函数中可以检查主函数启动时的 参数
OutputDebugString(StrFormat("theParamName = \"%s\", theParamValue = \"%s\"",
theParamName.c_str(), theParamValue.c_str()).c_str());
}
//res.cpp
#include "Res.h"
#include "SexyAppFramework/ResourceManager.h"
using namespace Sexy;
#pragma warning(disable:4311 4312)
static bool gNeedRecalcVariableToIdMap = false;
bool Sexy::ExtractResourcesByName(ResourceManager *theManager, const char *theName)
{
if (strcmp(theName,"Game")==0) return ExtractGameResources(theManager);
if (strcmp(theName,"Hungarr")==0) return ExtractHungarrResources(theManager);
if (strcmp(theName,"Init")==0) return ExtractInitResources(theManager);
if (strcmp(theName,"TitleScreen")==0) return ExtractTitleScreenResources(theManager);
return false;
}
// 把一个字符串ID映射到对应的整数ID.
Sexy::ResourceId Sexy::GetIdByStringId(const char *theStringId)
{
typedef std::map<std::string,int> MyMap;
static MyMap aMap;
if(aMap.empty())
{
for(int i=0; i<RESOURCE_ID_MAX; i++)
aMap[GetStringIdById(i)] = i;
}
MyMap::iterator anItr = aMap.find(theStringId);
if (anItr == aMap.end())
return RESOURCE_ID_MAX;
else
return (ResourceId) anItr->second;
}
// Game Resources
Image* Sexy::IMAGE_BG0;
Image* Sexy::IMAGE_BG1;
Image* Sexy::IMAGE_BG2;
Image* Sexy::IMAGE_BUTTON_DOWN;
Image* Sexy::IMAGE_BUTTON_NORMAL;
Image* Sexy::IMAGE_BUTTON_OVER;
Image* Sexy::IMAGE_CHECKBOX;
Image* Sexy::IMAGE_DIALOG_BOX;
Image* Sexy::IMAGE_DIALOG_BUTTON;
Image* Sexy::IMAGE_ROBOTROBOT;
Image* Sexy::IMAGE_SLIDER_THUMB;
Image* Sexy::IMAGE_SLIDER_TRACK;
int Sexy::SOUND_MUTATOR;
int Sexy::SOUND_TIMER;
bool Sexy::ExtractGameResources(ResourceManager *theManager)
{
gNeedRecalcVariableToIdMap = true;
ResourceManager &aMgr = *theManager;
try
{
IMAGE_BG0 = aMgr.GetImageThrow("IMAGE_BG0");
IMAGE_BG1 = aMgr.GetImageThrow("IMAGE_BG1");
IMAGE_BG2 = aMgr.GetImageThrow("IMAGE_BG2");
IMAGE_BUTTON_DOWN = aMgr.GetImageThrow("IMAGE_BUTTON_DOWN");
IMAGE_BUTTON_NORMAL = aMgr.GetImageThrow("IMAGE_BUTTON_NORMAL");
IMAGE_BUTTON_OVER = aMgr.GetImageThrow("IMAGE_BUTTON_OVER");
IMAGE_CHECKBOX = aMgr.GetImageThrow("IMAGE_CHECKBOX");
IMAGE_DIALOG_BOX = aMgr.GetImageThrow("IMAGE_DIALOG_BOX");
IMAGE_DIALOG_BUTTON = aMgr.GetImageThrow("IMAGE_DIALOG_BUTTON");
IMAGE_ROBOTROBOT = aMgr.GetImageThrow("IMAGE_ROBOTROBOT");
IMAGE_SLIDER_THUMB = aMgr.GetImageThrow("IMAGE_SLIDER_THUMB");
IMAGE_SLIDER_TRACK = aMgr.GetImageThrow("IMAGE_SLIDER_TRACK");
SOUND_MUTATOR = aMgr.GetSoundThrow("SOUND_MUTATOR");
SOUND_TIMER = aMgr.GetSoundThrow("SOUND_TIMER");
}
catch(ResourceManagerException&)
{
return false;
}
return true;
}
// Hungarr Resources
Image* Sexy::IMAGE_ATOMIC_EXPLOSION;
Image* Sexy::IMAGE_BOMB_RADIAL_DEATH;
Image* Sexy::IMAGE_HUNGARR_BEAM_DOWN;
Image* Sexy::IMAGE_HUNGARR_BEAM_LEFT;
Image* Sexy::IMAGE_HUNGARR_BEAM_RIGHT;
Image* Sexy::IMAGE_HUNGARR_BEAM_UP;
Image* Sexy::IMAGE_HUNGARR_HORIZ;
Image* Sexy::IMAGE_HUNGARR_SMALL;
Image* Sexy::IMAGE_HUNGARR_VERT;
Image* Sexy::IMAGE_PARTICLE_LIGHTNING;
Image* Sexy::IMAGE_PLANETS;
Image* Sexy::IMAGE_SPARK;
int Sexy::SOUND_BEAM_HIT;
int Sexy::SOUND_BEAM_MOVING;
int Sexy::SOUND_BUTTON;
int Sexy::SOUND_EXPLOSION;
int Sexy::SOUND_GAME_OVER_CLICK;
int Sexy::SOUND_GAME_OVER_RESTART;
int Sexy::SOUND_GAME_OVER_STATS;
int Sexy::SOUND_GAME_OVER_TEXT;
int Sexy::SOUND_LEVEL_UP1;
int Sexy::SOUND_LEVEL_UP2;
int Sexy::SOUND_LEVEL_UP3;
int Sexy::SOUND_LEVEL_UP4;
int Sexy::SOUND_MAGZAP;
int Sexy::SOUND_PLANET;
int Sexy::SOUND_PLANET_HIT;
int Sexy::SOUND_REGION_FILLED;
bool Sexy::ExtractHungarrResources(ResourceManager *theManager)
{
gNeedRecalcVariableToIdMap = true;
ResourceManager &aMgr = *theManager;
try
{
IMAGE_ATOMIC_EXPLOSION = aMgr.GetImageThrow("IMAGE_ATOMIC_EXPLOSION");
IMAGE_BOMB_RADIAL_DEATH = aMgr.GetImageThrow("IMAGE_BOMB_RADIAL_DEATH");
IMAGE_HUNGARR_BEAM_DOWN = aMgr.GetImageThrow("IMAGE_HUNGARR_BEAM_DOWN");
IMAGE_HUNGARR_BEAM_LEFT = aMgr.GetImageThrow("IMAGE_HUNGARR_BEAM_LEFT");
IMAGE_HUNGARR_BEAM_RIGHT = aMgr.GetImageThrow("IMAGE_HUNGARR_BEAM_RIGHT");
IMAGE_HUNGARR_BEAM_UP = aMgr.GetImageThrow("IMAGE_HUNGARR_BEAM_UP");
IMAGE_HUNGARR_HORIZ = aMgr.GetImageThrow("IMAGE_HUNGARR_HORIZ");
IMAGE_HUNGARR_SMALL = aMgr.GetImageThrow("IMAGE_HUNGARR_SMALL");
IMAGE_HUNGARR_VERT = aMgr.GetImageThrow("IMAGE_HUNGARR_VERT");
IMAGE_PARTICLE_LIGHTNING = aMgr.GetImageThrow("IMAGE_PARTICLE_LIGHTNING");
IMAGE_PLANETS = aMgr.GetImageThrow("IMAGE_PLANETS");
IMAGE_SPARK = aMgr.GetImageThrow("IMAGE_SPARK");
SOUND_BEAM_HIT = aMgr.GetSoundThrow("SOUND_BEAM_HIT");
SOUND_BEAM_MOVING = aMgr.GetSoundThrow("SOUND_BEAM_MOVING");
SOUND_BUTTON = aMgr.GetSoundThrow("SOUND_BUTTON");
SOUND_EXPLOSION = aMgr.GetSoundThrow("SOUND_EXPLOSION");
SOUND_GAME_OVER_CLICK = aMgr.GetSoundThrow("SOUND_GAME_OVER_CLICK");
SOUND_GAME_OVER_RESTART = aMgr.GetSoundThrow("SOUND_GAME_OVER_RESTART");
SOUND_GAME_OVER_STATS = aMgr.GetSoundThrow("SOUND_GAME_OVER_STATS");
SOUND_GAME_OVER_TEXT = aMgr.GetSoundThrow("SOUND_GAME_OVER_TEXT");
SOUND_LEVEL_UP1 = aMgr.GetSoundThrow("SOUND_LEVEL_UP1");
SOUND_LEVEL_UP2 = aMgr.GetSoundThrow("SOUND_LEVEL_UP2");
SOUND_LEVEL_UP3 = aMgr.GetSoundThrow("SOUND_LEVEL_UP3");
SOUND_LEVEL_UP4 = aMgr.GetSoundThrow("SOUND_LEVEL_UP4");
SOUND_MAGZAP = aMgr.GetSoundThrow("SOUND_MAGZAP");
SOUND_PLANET = aMgr.GetSoundThrow("SOUND_PLANET");
SOUND_PLANET_HIT = aMgr.GetSoundThrow("SOUND_PLANET_HIT");
SOUND_REGION_FILLED = aMgr.GetSoundThrow("SOUND_REGION_FILLED");
}
catch(ResourceManagerException&)
{
return false;
}
return true;
}
// Init Resources
Font* Sexy::FONT_DEFAULT;
Font* Sexy::FONT_HUNGARR;
Image* Sexy::IMAGE_CUSTOM_DRAGGING;
Image* Sexy::IMAGE_CUSTOM_HAND;
Image* Sexy::IMAGE_CUSTOM_POINTER;
Image* Sexy::IMAGE_CUSTOM_TEXT;
Image* Sexy::IMAGE_HUNGARR_LOGO;
bool Sexy::ExtractInitResources(ResourceManager *theManager)
{
gNeedRecalcVariableToIdMap = true;
ResourceManager &aMgr = *theManager;
try
{
FONT_DEFAULT = aMgr.GetFontThrow("FONT_DEFAULT");
FONT_HUNGARR = aMgr.GetFontThrow("FONT_HUNGARR");
IMAGE_CUSTOM_DRAGGING = aMgr.GetImageThrow("IMAGE_CUSTOM_DRAGGING");
IMAGE_CUSTOM_HAND = aMgr.GetImageThrow("IMAGE_CUSTOM_HAND");
IMAGE_CUSTOM_POINTER = aMgr.GetImageThrow("IMAGE_CUSTOM_POINTER");
IMAGE_CUSTOM_TEXT = aMgr.GetImageThrow("IMAGE_CUSTOM_TEXT");
IMAGE_HUNGARR_LOGO = aMgr.GetImageThrow("IMAGE_HUNGARR_LOGO");
}
catch(ResourceManagerException&)
{
return false;
}
return true;
}
// TitleScreen Resources
Image* Sexy::IMAGE_LOADER_BAR;
Image* Sexy::IMAGE_LOADER_LOADINGTXT;
int Sexy::SOUND_CONTINUE;
bool Sexy::ExtractTitleScreenResources(ResourceManager *theManager)
{
gNeedRecalcVariableToIdMap = true;
ResourceManager &aMgr = *theManager;
try
{
IMAGE_LOADER_BAR = aMgr.GetImageThrow("IMAGE_LOADER_BAR");
IMAGE_LOADER_LOADINGTXT = aMgr.GetImageThrow("IMAGE_LOADER_LOADINGTXT");
SOUND_CONTINUE = aMgr.GetSoundThrow("SOUND_CONTINUE");
}
catch(ResourceManagerException&)
{
return false;
}
return true;
}
static void* gResources[] =
{
&FONT_DEFAULT,
&FONT_HUNGARR,
&IMAGE_CUSTOM_POINTER,
&IMAGE_CUSTOM_HAND,
&IMAGE_CUSTOM_DRAGGING,
&IMAGE_CUSTOM_TEXT,
&IMAGE_HUNGARR_LOGO,
&IMAGE_LOADER_BAR,
&IMAGE_LOADER_LOADINGTXT,
&SOUND_CONTINUE,
&SOUND_MUTATOR,
&SOUND_TIMER,
&IMAGE_ROBOTROBOT,
&IMAGE_CHECKBOX,
&IMAGE_BG0,
&IMAGE_BG1,
&IMAGE_BG2,
&IMAGE_BUTTON_DOWN,
&IMAGE_BUTTON_OVER,
&IMAGE_BUTTON_NORMAL,
&IMAGE_DIALOG_BOX,
&IMAGE_DIALOG_BUTTON,
&IMAGE_SLIDER_TRACK,
&IMAGE_SLIDER_THUMB,
&IMAGE_HUNGARR_SMALL,
&IMAGE_HUNGARR_BEAM_UP,
&IMAGE_HUNGARR_BEAM_DOWN,
&IMAGE_HUNGARR_BEAM_LEFT,
&IMAGE_HUNGARR_BEAM_RIGHT,
&IMAGE_HUNGARR_HORIZ,
&IMAGE_HUNGARR_VERT,
&IMAGE_ATOMIC_EXPLOSION,
&IMAGE_BOMB_RADIAL_DEATH,
&IMAGE_PLANETS,
&IMAGE_SPARK,
&IMAGE_PARTICLE_LIGHTNING,
&SOUND_MAGZAP,
&SOUND_BUTTON,
&SOUND_PLANET,
&SOUND_LEVEL_UP1,
&SOUND_LEVEL_UP2,
&SOUND_EXPLOSION,
&SOUND_BEAM_HIT,
&SOUND_PLANET_HIT,
&SOUND_BEAM_MOVING,
&SOUND_LEVEL_UP4,
&SOUND_LEVEL_UP3,
&SOUND_GAME_OVER_CLICK,
&SOUND_GAME_OVER_STATS,
&SOUND_GAME_OVER_RESTART,
&SOUND_GAME_OVER_TEXT,
&SOUND_REGION_FILLED,
NULL
};
Image* Sexy::LoadImageById(ResourceManager *theManager, int theId)
{
return (*((Image**)gResources[theId]) = theManager->LoadImage(GetStringIdById(theId)));
}
Image* Sexy::GetImageById(int theId)
{
return *(Image**)gResources[theId];
}
Font* Sexy::GetFontById(int theId)
{
return *(Font**)gResources[theId];
}
int Sexy::GetSoundById(int theId)
{
return *(int*)gResources[theId];
}
static Sexy::ResourceId GetIdByVariable(const void *theVariable)
{
typedef std::map<int,int> MyMap;
static MyMap aMap;
if(gNeedRecalcVariableToIdMap)
{
gNeedRecalcVariableToIdMap = false;
aMap.clear();
for(int i=0; i<RESOURCE_ID_MAX; i++)
aMap[*(int*)gResources[i]] = i;
}
MyMap::iterator anItr = aMap.find((int)theVariable);
if (anItr == aMap.end())
return RESOURCE_ID_MAX;
else
return (ResourceId) anItr->second;
}
Sexy::ResourceId Sexy::GetIdByImage(Image *theImage)
{
return GetIdByVariable(theImage);
}
Sexy::ResourceId Sexy::GetIdByFont(Font *theFont)
{
return GetIdByVariable(theFont);
}
Sexy::ResourceId Sexy::GetIdBySound(int theSound)
{
return GetIdByVariable((void*)theSound);
}
const char* Sexy::GetStringIdById(int theId)
{
switch(theId)
{
case FONT_DEFAULT_ID: return "FONT_DEFAULT";
case FONT_HUNGARR_ID: return "FONT_HUNGARR";
case IMAGE_CUSTOM_POINTER_ID: return "IMAGE_CUSTOM_POINTER";
case IMAGE_CUSTOM_HAND_ID: return "IMAGE_CUSTOM_HAND";
case IMAGE_CUSTOM_DRAGGING_ID: return "IMAGE_CUSTOM_DRAGGING";
case IMAGE_CUSTOM_TEXT_ID: return "IMAGE_CUSTOM_TEXT";
case IMAGE_HUNGARR_LOGO_ID: return "IMAGE_HUNGARR_LOGO";
case IMAGE_LOADER_BAR_ID: return "IMAGE_LOADER_BAR";
case IMAGE_LOADER_LOADINGTXT_ID: return "IMAGE_LOADER_LOADINGTXT";
case SOUND_CONTINUE_ID: return "SOUND_CONTINUE";
case SOUND_MUTATOR_ID: return "SOUND_MUTATOR";
case SOUND_TIMER_ID: return "SOUND_TIMER";
case IMAGE_ROBOTROBOT_ID: return "IMAGE_ROBOTROBOT";
case IMAGE_CHECKBOX_ID: return "IMAGE_CHECKBOX";
case IMAGE_BG0_ID: return "IMAGE_BG0";
case IMAGE_BG1_ID: return "IMAGE_BG1";
case IMAGE_BG2_ID: return "IMAGE_BG2";
case IMAGE_BUTTON_DOWN_ID: return "IMAGE_BUTTON_DOWN";
case IMAGE_BUTTON_OVER_ID: return "IMAGE_BUTTON_OVER";
case IMAGE_BUTTON_NORMAL_ID: return "IMAGE_BUTTON_NORMAL";
case IMAGE_DIALOG_BOX_ID: return "IMAGE_DIALOG_BOX";
case IMAGE_DIALOG_BUTTON_ID: return "IMAGE_DIALOG_BUTTON";
case IMAGE_SLIDER_TRACK_ID: return "IMAGE_SLIDER_TRACK";
case IMAGE_SLIDER_THUMB_ID: return "IMAGE_SLIDER_THUMB";
case IMAGE_HUNGARR_SMALL_ID: return "IMAGE_HUNGARR_SMALL";
case IMAGE_HUNGARR_BEAM_UP_ID: return "IMAGE_HUNGARR_BEAM_UP";
case IMAGE_HUNGARR_BEAM_DOWN_ID: return "IMAGE_HUNGARR_BEAM_DOWN";
case IMAGE_HUNGARR_BEAM_LEFT_ID: return "IMAGE_HUNGARR_BEAM_LEFT";
case IMAGE_HUNGARR_BEAM_RIGHT_ID: return "IMAGE_HUNGARR_BEAM_RIGHT";
case IMAGE_HUNGARR_HORIZ_ID: return "IMAGE_HUNGARR_HORIZ";
case IMAGE_HUNGARR_VERT_ID: return "IMAGE_HUNGARR_VERT";
case IMAGE_ATOMIC_EXPLOSION_ID: return "IMAGE_ATOMIC_EXPLOSION";
case IMAGE_BOMB_RADIAL_DEATH_ID: return "IMAGE_BOMB_RADIAL_DEATH";
case IMAGE_PLANETS_ID: return "IMAGE_PLANETS";
case IMAGE_SPARK_ID: return "IMAGE_SPARK";
case IMAGE_PARTICLE_LIGHTNING_ID: return "IMAGE_PARTICLE_LIGHTNING";
case SOUND_MAGZAP_ID: return "SOUND_MAGZAP";
case SOUND_BUTTON_ID: return "SOUND_BUTTON";
case SOUND_PLANET_ID: return "SOUND_PLANET";
case SOUND_LEVEL_UP1_ID: return "SOUND_LEVEL_UP1";
case SOUND_LEVEL_UP2_ID: return "SOUND_LEVEL_UP2";
case SOUND_EXPLOSION_ID: return "SOUND_EXPLOSION";
case SOUND_BEAM_HIT_ID: return "SOUND_BEAM_HIT";
case SOUND_PLANET_HIT_ID: return "SOUND_PLANET_HIT";
case SOUND_BEAM_MOVING_ID: return "SOUND_BEAM_MOVING";
case SOUND_LEVEL_UP4_ID: return "SOUND_LEVEL_UP4";
case SOUND_LEVEL_UP3_ID: return "SOUND_LEVEL_UP3";
case SOUND_GAME_OVER_CLICK_ID: return "SOUND_GAME_OVER_CLICK";
case SOUND_GAME_OVER_STATS_ID: return "SOUND_GAME_OVER_STATS";
case SOUND_GAME_OVER_RESTART_ID: return "SOUND_GAME_OVER_RESTART";
case SOUND_GAME_OVER_TEXT_ID: return "SOUND_GAME_OVER_TEXT";
case SOUND_REGION_FILLED_ID: return "SOUND_REGION_FILLED";
default: return "";
}
}
titleScreen.cpp
#include "TitleScreen.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"
#include "SexyAppFramework/Font.h"
#include "SexyAppFramework/Graphics.h"
#include "SexyAppFramework/Image.h"
#include "SexyAppFramework/WidgetManager.h"
#include "SexyAppFramework/Rect.h"
// 本例子使用一个新类 HyperlinkWidget(超连接部件) . 它看上去就像 www 的超连接.
#include "SexyAppFramework/HyperlinkWidget.h"
using namespace Sexy;
//
// 重写基类的 Init()
// 这里只是 创建了一个超连接部件.
void TitleScreen::Init(void)
{
// 超连接类和按钮类都是用 ButtonListener 作为监听器.
// 它们的创建也类似. 构造函数的参数为 组件ID, 监听器对象
mContinueLink = new HyperlinkWidget(1, this);
// 设置超连接部件的字体.
// 这和第3个 demo 中给按钮设置字体是一样的.
// 只是这里的字体是通过资源管理器加载的. 而字体对象的名字FONT_DEFAULT也是在xml文件中指定的.
mContinueLink->SetFont(FONT_DEFAULT);
// 设置超连接的文字
mContinueLink->mLabel = _S("CLICK TO CONTINUE");
// 和按钮一样. 我们可以设置超连接的缺省颜色和鼠标经过颜色.
// 我们没有使用 SetColor() 函数. 而是直接设置 mColor 和 mOverColor 两个成员.
mContinueLink->mColor = Color(255, 255, 255); //缺省色为白色
mContinueLink->mOverColor = Color(0, 255, 0); //鼠标经过颜色为绿色
mContinueLink->mUnderlineSize = 1; // 指定超连接下划线的宽度
}
//
// 将Init()中创建的超连接部件也放入WidgetManager
void TitleScreen::AddedToManager(WidgetManager *theWidgetManager)
{
Widget::AddedToManager(theWidgetManager);
// 在所有资源被加载完之前. 我们应该设置该超连接部件为不可见的.
// 因为当用户点击该部件后就会进入游戏. 然而资源还没加载好...
mContinueLink->SetVisible(false);
// 而且即使隐藏起来也不行. 因为虽然看不见. 但技术上用户还是有可能点击到它.
// 所以我们还要设置它为不可用的.
mContinueLink->SetDisabled(true);
// 计算并设置超连接部件的位置/大小.
int labelWidth = FONT_DEFAULT->StringWidth(mContinueLink->mLabel);
int labelHeight = FONT_DEFAULT->GetHeight();
mContinueLink->Resize( mWidth / 2 - labelWidth / 2,
mHeight - labelHeight - 40,
labelWidth,
labelHeight+4);
// 下边一行将 mDoFinger 成员设置为 ture.
// 表示鼠标移动到该控件时. WidgetManager 自动将光标图像由箭头变为手形.
// 不只是超连接部件. 所有的部件都有该功能.
mContinueLink->mDoFinger = true;
// 最后将该超连接部件也加入WidgetManager.
theWidgetManager->AddWidget(mContinueLink);
}
void TitleScreen::RemovedFromManager(WidgetManager *theWidgetManager)
{
Widget::RemovedFromManager(theWidgetManager);
theWidgetManager->RemoveWidget(mContinueLink);
}
//
void TitleScreen::Draw(Graphics *g)
{
g->SetColor(Color::Black); //黑色(Black)和白色(White)是 Color 的两个静态成员. 方便使用.
g->FillRect(0, 0, mWidth, mHeight);
// 绘制 进度条(实际是缩放绘制一个图片)
// 资源加载的百分比可以用 mApp->GetLoadingThreadProgress() 得到. 返回值是从0.0 - 1.0的小数.
int loaderBarWidth = IMAGE_LOADER_BAR->GetWidth();
int drawWidth = (int)(mApp->GetLoadingThreadProgress() * loaderBarWidth);
if (drawWidth > 0)
{
g->DrawImage(IMAGE_LOADER_BAR, mWidth / 2 - loaderBarWidth / 2,
400,
Rect(0, 0, drawWidth, IMAGE_LOADER_BAR->GetHeight()));
}
// 在加载完成前. 显示 "loding.." 图片
if (mContinueLink->mVisible == false)
g->DrawImage(IMAGE_LOADER_LOADINGTXT, mContinueLink->mX, mContinueLink->mY - 20);
}
//
//这个函数将在 GameApp::LoadingThreadCompleted() 中手动调用.
void TitleScreen::LoadingComplete()
{
// 使超连接变为可用
mContinueLink->SetVisible(true);
mContinueLink->SetDisabled(false);
}
//
//处理超连接事件
void TitleScreen::ButtonDepress(int theId)
{
if (theId == 1)
{
// 点击超连接离开前导屏幕.
// 这时应该从部件管理器中 remove 不再用到的东西.
mApp->mWidgetManager->RemoveWidget(this);
mApp->mWidgetManager->RemoveWidget(mContinueLink);
// 然后这两个部件应该被delete. 但现在还在使用它们.
// 所以用 SexyAppBase::SafeDeleteWidget() 来将这个动作放入消息队列以后执行.
mApp->SafeDeleteWidget(this);
mApp->SafeDeleteWidget(mContinueLink);
mContinueLink = NULL;
// 现在告诉游戏程序类. 应该开始board部件了.
mApp->TitleScreenIsFinished();
}
}
popcap sexyframework- Demo4 使用资源管理器加载资源 前导屏幕 播放声音 以及超连接 输入框 列表框 滚动条
最新推荐文章于 2021-07-29 08:00:38 发布