popcap sexyframework- Demo4 使用资源管理器加载资源 前导屏幕 播放声音 以及超连接 输入框 列表框 滚动条

来源:互联网 发布:js页面时间格式化 编辑:程序博客网 时间:2024/06/07 10:31
//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 ResourcesImage* 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 ResourcesImage* 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 ResourcesFont* 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 ResourcesImage* 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()中创建的超连接部件也放入WidgetManagervoid 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(); }}

原创粉丝点击