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

//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();
 }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值