//board.h #ifndef __BOARD_H__ #define __BOARD_H__ #include "SexyAppFramework/Widget.h" class Graphics; class GameApp; class Board : public Widget { GameApp* mApp; int mPulseAmt; // 一个在每次Update()时波动的值. 用于在Draw()时构造变化的颜色. bool mIncPulse; // 用来控制前边的 mPulseAmt 变大还是变小. public: Board(GameApp* theApp){ mApp = theApp; mPulseAmt = 255; mIncPulse = false; } virtual ~Board(){} virtual void Draw(Graphics* g); virtual void Update(); }; #endif // __BOARD_H__ //gameapp.h #ifndef __GAMEAPP_H__ #define __GAMEAPP_H__ #include "SexyAppFramework/SexyAppBase.h" class Board; //图像和字体的类 class ImageFont; class Image; class GameApp : public SexyAppBase { Board* mBoard; public: // 资源 ImageFont* mTextFont; ImageFont* mNumberFont; Image* mOpaqueBeamImg; Image* mMoonImg; Image* mTurbotImg; public: GameApp(); virtual ~GameApp(); virtual void Init(); virtual void LoadingThreadProc(); virtual void LoadingThreadCompleted(); }; #endif // __GAMEAPP_H__ //board.cpp #include "Board.h" #include "GameApp.h" #include "SexyAppFramework/Graphics.h" // See the Draw method for more information on using the Color class. #include "SexyAppFramework/Color.h" // Why are we including ImageFont.h and not Font.h? Font.h is just a generic // base class. ImageFont creates fonts from an image that contains all the // text characters as well as a text file that indicates character widths // and kerning information, as well as some more advanced features not used // in this tutorial such as font layers, etc. #include "SexyAppFramework/ImageFont.h" // The Image.h file just declares basic functions. All images are either of // the DDImage or MemoryImage type. For this demo, we will use DDImage // types, as they are the type returned by the image loading code. // A DDImage is actually derived from MemoryImage, so where an Image or // MemoryImage is required, a DDImage will suffice as well. A DDImage // contains optimized code for use with DirectX 7+. #include "SexyAppFramework/DDImage.h" // The Rectangle template, used to specify X, Y, Width, Height #include "SexyAppFramework/Rect.h" // The SexyAppFramework resides in the "Sexy" namespace. As a convenience, // you'll see in all the .cpp files "using namespace Sexy" to avoid // having to prefix everything with Sexy:: using namespace Sexy; void Board::Update() { Widget::Update(); // 这个函数每秒被调用100次. 每次更新 mPulseAmt 变量. 使其在0-255间变化. // 在后边 Draw() 时根据该变量的值绘画. if (mIncPulse) { if (++mPulseAmt >= 255) { mIncPulse = false; mPulseAmt = 255; } } else { if (--mPulseAmt <= 0) { mIncPulse = true; mPulseAmt = 0; } } // mUpdateCnt 是Update()被调用的次数. 下边让它每2秒播放声音一次. if (mUpdateCnt % 200 == 0) mApp->PlaySample(1); //播放声音. 参数为GameApp::LoadingThreadProc()加载资源时指定的ID. //播放时还可以指定左右声道. 以及音量等. 这里就不演示了. // 使窗体组件变脏. // 它经常在 Update() 中调用. 来要求屏幕重新绘制. (通常在更改了逻辑之后). 但也可以在其他函数中调用它. MarkDirty(); } void Board::Draw(Graphics* g) { g->SetColor(Color(0, 0, 0)); //指定颜色为黑色. 用3个参数构造颜色时表示alpha为255. g->FillRect(0, 0, mWidth, mHeight); ///////////////////////////////////////////////////// // 绘制文字. // 在绘制之前. 我们要用 SetFont() 指定 Graphics 使用的字体. // 并且要指定绘制文字使用的颜色. 缺省的是白色. g->SetColor(Color(255, 0, 0)); g->SetFont(mApp->mTextFont); g->DrawString(_S("Woo! Text! Ya!"), 10, 10); //这里指定的Y坐标为10. //但实际绘制时. 不会从10开始. //因为字体的基准Y位置有一些偏移. //为了演示. 现在我们为Graphics重新指定一种字体. //在这个字体中只包含数字和几个符号. 可以查看其对应的资源文件图像. //它只有 0123456789+-,; g->SetFont(mApp->mNumberFont); g->SetColor(Color(255, 255, 0)); g->DrawString(_S("+200"), 10, 40); g->DrawString(_S("+200 pts"), 10, 60); //可以看到.当字符串中包含字体中没有的字符时. 会跳过该字符. //SexString 是common.h中定义的一个typedef. 根据当前是否使用宽字符而表示string或wstring. SexyString myString = StrFormat(_S("You got %d points!"), 147); g->SetFont(mApp->mTextFont); g->DrawString(myString, 10, 80); g->SetColor(Color(0, 255, 0)); // 我们也可以取得一个字符串在给定字体时的宽度(象素). // 如下一行先用 mTextFont->StringWidth() 取得 myString 占的宽度. 然后在该位置绘制另一个字符串. g->DrawString(_S(" I am to the right of that previous string."), 10 + mApp->mTextFont->StringWidth(myString), 80); // 可以指定更多格式的绘制函数: // WriteString() // 它的最后一个参数可以为 -1, 0, 1 分别表示左/中/右对齐 WriteString(g, _S("Left justified at X of 200"), 200, 95, -1, -1); WriteString(g, _S("Centered using app width"), 0, 110, mWidth, 0); WriteString(g, _S("Centered using width of 200, X of 200"), 200, 125, mWidth, 1); //WriteWordWrapped() //把要绘制的文字放入一个指定的举行区域. WriteWordWrapped(g, Rect(30, 140, 200, 400), _S("This is some text that is wrapped inside of the rectangle \ at X of 30, Y of 140, width of 200, height of 400. It has \ been wrapped using the default left justification and the \ default line spacing."), -1, -1); /////////////////////////////////////////////////// //绘制图像 // 最简单的: 只要指定图像的指针以及图像的位置. g->DrawImage(mApp->mTurbotImg, 230, 140); /////////////////////////////////////////////////// // 用指定的颜色绘制图像 // 例如我们把上一个绘制的图像改成红色的. // 需要注意的是: // 1. 这种绘制比正常绘制图像要慢. // 2. 在绘制前要调用 g->SetColorizeImages(true); // 3. 在改变颜色的绘制语句结束后. 要调用g->SetColorizeImages(false); // 指定要改变的颜色值.本例是红色. g->SetColor(Color(255, 0, 0)); g->SetColorizeImages(true); g->DrawImage(mApp->mTurbotImg, 230, 140 + mApp->mTurbotImg->GetHeight()); g->SetColorizeImages(false); ////////////////////////////////////////////////////// // 附加绘制 // 即在原有的图像上再绘制另一个图像. // 例如下边我们把这个图像画2次. 让它看起来明亮一些. // 需要注意的是. 附加绘制比前边的用指定颜色绘制. 还要慢些. g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth(), 140); // 设置绘制模式为附加绘制 g->SetDrawMode(Graphics::DRAWMODE_ADDITIVE); g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth(), 140); // 设置绘制模式为正常绘制 g->SetDrawMode(Graphics::DRAWMODE_NORMAL); ///////////////////////////////////////////////////////// // 下一个是用附加绘制和指定颜色绘制结合起来的应用. // 它用一个不断变化的值来构造颜色. 再用该颜色附加绘制该图像. // 使该图像的色彩不断变化. //先正常绘制一次 g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth() * 2, 140); //再用指定颜色的方法 附加绘制一次. g->SetDrawMode(Graphics::DRAWMODE_ADDITIVE); g->SetColor(Color(mPulseAmt, mPulseAmt, mPulseAmt)); //mPulseAmt在Update()中一直变化. g->SetColorizeImages(true); g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth() * 2, 140); g->SetDrawMode(Graphics::DRAWMODE_NORMAL); g->SetColorizeImages(false); //////////////////////////////////////////////// // 使用 alpha 绘制 // 下边的例子用指定颜色绘制时. 指定的颜色是半透明的白色. // 这样绘制图像的效果是: 该图像也成为半透明的了. // 先绘制一副图像. g->DrawImage(mApp->mMoonImg, 400, 300); // 再用半透明的办法绘制另一幅图像 g->SetColor(Color(255, 255, 255, 60)); //指定一个半透明的白色. g->SetColorizeImages(true); g->DrawImage(mApp->mTurbotImg, 400, 300); //用半透明的白色绘制图像。 g->SetColorizeImages(false); } //gameapp.cpp #include "GameApp.h" #include "Board.h" #include "SexyAppFramework/WidgetManager.h" // 用 ImageFont.h 而不是 Font.h. // 因为 Font 是 ImageFont 的基类. ImageFont从图像创建字体. #include "SexyAppFramework/ImageFont.h" // DDImage 类 派生自 MemoryImage类. 而 MemoryImage 派生自 Image 类. #include "SexyAppFramework/DDImage.h" // 音效类 #include "SexyAppFramework/SoundManager.h" using namespace Sexy; GameApp::GameApp() { mWidth = 800; mHeight = 600; mBoard = NULL; mTextFont = NULL; mNumberFont = NULL; mTurbotImg = NULL; mMoonImg = NULL; mOpaqueBeamImg = NULL; } ////////////////////////////////////////////////////////////////////////// GameApp::~GameApp() { mWidgetManager->RemoveWidget(mBoard); delete mBoard; //必须先执行上句 delete mTextFont; delete mNumberFont; delete mTurbotImg; delete mMoonImg; delete mOpaqueBeamImg; mSoundManager->ReleaseSounds(); //释放所有音效占用的内存 } void GameApp::Init() { SexyAppBase::Init(); } void GameApp::LoadingThreadProc() { // You need to clean up the memory allocated by these functions yourself. // 加载图像资源很简单. // 它支持的图像格式有: Targa, JPEG, PNG, GIF. // 加载时可以不指定扩展名. 而让程序自动判断. 例如: mOpaqueBeamImg = GetImage("images/beam_opaque"); //不指定扩展名. 而让程序自动判断. // 像 JPEG 这样的图片没有alpha信息. 所以用一个 "同名加_" 的黑白图像来做为它的alpha信息. // 缺省的. 加载一个图像时程序会自动检查该图像对应的alpha信息. // 但现在用PNG格式的图像. 不用那么麻烦了. //另外. GetImage() 得到的图像的指针必须手动删除. 见本例子中的本类的析够函数. // 如果上一步中. 资源加载失败. GetImage()会返回 NULL. // 一定要检查这个返回值. 然后设置 mLoadingFailed 为true. 并返回错误信息退出. if (mOpaqueBeamImg == NULL) { // PopUp() 相当于一个 MessageBox() Popup("There was an error loading the file: images/beam_opaque"); mLoadingFailed = true; return; } mMoonImg = GetImage("images/moon"); if (mMoonImg == NULL) { Popup("There was an error loading the file: images/moon"); mLoadingFailed = true; return; } mTurbotImg = GetImage("images/turbot_worry"); if (mTurbotImg == NULL) { Popup("There was an error loading the file: images/turbot_worry"); mLoadingFailed = true; return; } //加载图像后应该调用它的 Palletize() 函数. 用来获得更好的性能. ((DDImage*)mOpaqueBeamImg)->Palletize(); ((DDImage*)mMoonImg)->Palletize(); ((DDImage*)mTurbotImg)->Palletize(); //////////////////////////////////////////////////// // 加载字体 // 每种字体资源由图像文件和文本文件组成. (两个文件名几乎相同. 除了图像文件名以_开始). // 加载时只要指出字体的文本文件名. 然后对应的图像文件也会被加载. 例如: mTextFont = new ImageFont(this, "fonts/Kiloton9.txt"); //检查 mTextFont->mFontData->mInitialized 来确认字体是否加载成功. if (!mTextFont->mFontData->mInitialized) { delete mTextFont; Popup("There was an error loading fonts/Kiloton9.txt"); mLoadingFailed = true; return; } mNumberFont = new ImageFont(this, "fonts/supernova20.txt"); if (!mNumberFont->mFontData->mInitialized) { delete mNumberFont; Popup("There was an error loading fonts/supernova20.txt"); mLoadingFailed = true; return; } ////////////////////////////////////// // 加载声音 // 如下例. 调用mSoundManager->LoadSound() 即可. // 参数为 该声音资源的ID(以后播放时就用该ID). 文件名. // 文件名可以不指定格式. 而让程序自动识别. 支持的格式有 WAV, OGG, AU, MP3 // 最后. 应该检查该函数的返回值. 确定声音文件是否加载成功. if (!mSoundManager->LoadSound(1, "sounds/timer")) { Popup("There was an error loading sounds/timer"); mLoadingFailed = true; return; } if (!mSoundManager->LoadSound(2, "sounds/mutator")) { Popup("There was an error loading sounds/mutator"); mLoadingFailed = true; return; } } void GameApp::LoadingThreadCompleted() { SexyAppBase::LoadingThreadCompleted(); // 检查在 LoadingThreadProc() 的时候是否设置mLoadingFailed if (mLoadingFailed) return; // 创建主窗口部件. mBoard = new Board(this); mBoard->Resize(0, 0, mWidth, mHeight); mWidgetManager->AddWidget(mBoard); // 播放声音 ID为2 PlaySample(2); } //main.cpp ////////////////////////////////////////////////////////////////////////// // 本例子学习 // // 加载/显示字体. // 加载/显示图像 // 加载/播放声音 // 按钮 / 监听器 / 事件 // * Colorizing images // * Additive drawing // * Palletizing images to use less RAM (when possible) // * Widget introduction: buttons, listeners, events, adding/removing ////////////////////////////////////////////////////////////////////////// #include "GameApp.h" using namespace Sexy; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { gHInstance = hInstance; GameApp* anApp = new GameApp(); anApp->Init(); anApp->Start(); delete anApp; return 0; }