Part2:Example
来源:互联网 发布:mac os iso光盘镜像 编辑:程序博客网 时间:2024/05/16 14:54
来自:http://www.codeproject.com/Articles/28608/TetroGL-An-OpenGL-Game-Tutorial-in-C-for-Win32-Pla
After all those explanations, it is time for a concrete example to see how we will use all those classes. The example will be quite simple and far from a complete game, but it shows the principles. The purpose is to have an animated character (a knight) that can be controlled through the direction keys. It moves in a simple scene: grass with some trees on it, in an isometric view. There is no collision detection yet, which means that the knight can move through the trees. Another thing that is not implemented is the order in which the sprites are drawn: the knight will always be drawn on top of the scene, no matter where he is, which is wrong in some situations (if he is behind a tree, the tree should be drawn on top of the knight). This is left as an exercise to the reader :).
All the code will be implemented in the CMainWindow
class. Let's first add some member variables in this class:
// The image for the grass. TImagePtr m_pGrassImg; // Images for the trees TImagePtr m_pTreesImg[16]; // The animated sprite of the knight CAnimatedSprite* m_pKnightSprite; // Which keys are currently pressed bool m_KeysDown[4]; // The last direction of the knight std::string m_strLastDir;
We first declare some
TImagePtr
which will hold several images that will be drawn (grass and trees). We then declare theCAnimatedSprite
which will be used to draw the knight. We finally have an array of 4 booleans to store the current state of the direction keys and astring
that contains the current direction of the knight. Those variables are initialized in the constructor of the main window class:// Load the grass image and set the color key. m_pGrassImg = CImage::CreateImage("GrassIso.bmp"); m_pGrassImg->GetTexture()->SetColorKey(0,128,128); // Load all the 'walk' animations for the knight. m_pKnightSprite = new CAnimatedSprite; CAnimFileLoader fileLoader1("KnightWalk.bmp", 8, 96, 96); CTextureManager::GetInstance()->GetTexture("KnightWalk.bmp") ->SetColorKey(111, 79, 51); m_pKnightSprite->AddAnimation("WalkE", fileLoader1.GetAnimation(0,7)); m_pKnightSprite->AddAnimation("WalkSE", fileLoader1.GetAnimation(8,15)); m_pKnightSprite->AddAnimation("WalkS", fileLoader1.GetAnimation(16,23)); m_pKnightSprite->AddAnimation("WalkSW", fileLoader1.GetAnimation(24,31)); m_pKnightSprite->AddAnimation("WalkW", fileLoader1.GetAnimation(32,39)); m_pKnightSprite->AddAnimation("WalkNW", fileLoader1.GetAnimation(40,47)); m_pKnightSprite->AddAnimation("WalkN", fileLoader1.GetAnimation(48,55)); m_pKnightSprite->AddAnimation("WalkNE", fileLoader1.GetAnimation(56,63)); // Load all the 'pause' animations for the knight. CAnimFileLoader fileLoader2("KnightPause.bmp", 8, 96, 96); CTextureManager::GetInstance()->GetTexture("KnightPause.bmp") ->SetColorKey(111, 79, 51); m_pKnightSprite->AddAnimation("PauseE", fileLoader2.GetAnimation(0,7)); m_pKnightSprite->AddAnimation("PauseSE", fileLoader2.GetAnimation(8,15)); m_pKnightSprite->AddAnimation("PauseS", fileLoader2.GetAnimation(16,23)); m_pKnightSprite->AddAnimation("PauseSW", fileLoader2.GetAnimation(24,31)); m_pKnightSprite->AddAnimation("PauseW", fileLoader2.GetAnimation(32,39)); m_pKnightSprite->AddAnimation("PauseNW", fileLoader2.GetAnimation(40,47)); m_pKnightSprite->AddAnimation("PauseN", fileLoader2.GetAnimation(48,55)); m_pKnightSprite->AddAnimation("PauseNE", fileLoader2.GetAnimation(56,63)); m_pKnightSprite->PlayAnimation("PauseE"); for (int i=0; i<4; i++) m_KeysDown[i] = false; // Set the initial direction to the east. m_strLastDir = "E"; m_pKnightSprite->SetPosition(350,250);
This looks like a lot of code but we need to load quite a bunch of animations for our knight: 2 animations (walk and pause) for each direction (8 different directions). We are using a new class here: theCAnimFileLoader
class. It is a simple helper class to easily load an image list from a file. It takes the file name, the number of images per row, the width and the height of an image as parameters in the constructor and you can retrieve an image list later by simply specifying the start index and the stop index of images in the file (it returns aCImageList
object). If you now look at the code, we first load the grass image and specify its color key, then we load all the 'walk' animations for our knight. Each animation name depends on the direction, e.g. for the 'walk' east direction, the animation name is "WalkE
". This will be used later to play a specific animation. We then specify that the default animation is the "PauseE
" animation.
Let's now look at how we handle the events when a key is pressed. This is done in theProcessEvent
function:
void CMainWindow::ProcessEvent(UINT Message, WPARAM wParam, LPARAM lParam){ switch (Message) { // Quit when we close the main window case WM_CLOSE : PostQuitMessage(0); break; case WM_SIZE: OnSize(LOWORD(lParam),HIWORD(lParam)); break; case WM_KEYDOWN : switch (wParam) { case VK_UP: m_KeysDown[0] = true; break; case VK_DOWN: m_KeysDown[1] = true; break; case VK_LEFT: m_KeysDown[2] = true; break; case VK_RIGHT: m_KeysDown[3] = true; break; } UpdateAnimation(); break; case WM_KEYUP : switch (wParam) { case VK_UP: m_KeysDown[0] = false; break; case VK_DOWN: m_KeysDown[1] = false; break; case VK_LEFT: m_KeysDown[2] = false; break; case VK_RIGHT: m_KeysDown[3] = false; break; } UpdateAnimation(); break; }}
As you can see, we handle the
WM_KEYDOWN
and theWM_KEYUP
messages, which correspond to a key pressed and a key released respectively. When such message is sent, theWPARAM
contains the code of the key which is pressed or released. We simply then set or reset the flag in our array to specify the state of the corresponding key (so, the first element in the array corresponds to the up key, the second to the down key, ...). We then call the UpdateAnimation
function:void CMainWindow::UpdateAnimation(){ // First check if at least one key is pressed bool keyPressed = false; for (int i=0; i<4; i++) { if (m_KeysDown[i]) { keyPressed = true; break; } } string strAnim; if (!keyPressed) strAnim = "Pause" + m_strLastDir; if (keyPressed) { string vertDir; string horizDir; if (m_KeysDown[0]) vertDir = "N"; else if (m_KeysDown[1]) vertDir = "S"; if (m_KeysDown[2]) horizDir = "W"; else if (m_KeysDown[3]) horizDir = "E"; m_strLastDir = vertDir + horizDir; strAnim = "Walk" + m_strLastDir; } m_pKnightSprite->PlayAnimation(strAnim);}We first check if at least one key is pressed. If that's not the case, we specify that the animation that should be played is "Pause" + the name of the last knight direction. If at least one key is pressed, we check which ones are pressed and we build the last direction
string
. Let's now look at theDraw
function:void CMainWindow::Draw(){ // Clear the buffer glClear(GL_COLOR_BUFFER_BIT); // Draw the grass int xPos=0, yPos=0; for (int i=0; i<8; i++) { for (int j=0; j<6; j++) { xPos = i * 256/2 - 128; if (i%2) yPos = (j * 128) - 128/2; else yPos = (j * 128); m_pGrassImg->BlitImage(xPos, yPos); } } // Draw some trees m_pTreesImg[0]->BlitImage(15,25); m_pTreesImg[1]->BlitImage(695,55); m_pTreesImg[2]->BlitImage(15,25); m_pTreesImg[3]->BlitImage(300,400); m_pTreesImg[4]->BlitImage(125,75); m_pTreesImg[5]->BlitImage(350,250); m_pTreesImg[6]->BlitImage(400,350); m_pTreesImg[7]->BlitImage(350,105); m_pTreesImg[8]->BlitImage(530,76); m_pTreesImg[9]->BlitImage(125,450); m_pTreesImg[10]->BlitImage(425,390); m_pTreesImg[11]->BlitImage(25,125); m_pTreesImg[12]->BlitImage(550,365); m_pTreesImg[13]->BlitImage(680,250); m_pTreesImg[14]->BlitImage(245,325); m_pTreesImg[15]->BlitImage(300,245); // Draw the knight m_pKnightSprite->DrawSprite(); // Move to the next frame of the animation m_pKnightSprite->NextFrame(); // Swap the buffers SwapBuffers(m_hDeviceContext);}We first draw the grass: if you open the GrassIso.bmp file, you can see that this is a losange, and not a rectangle. That shape is typically used for isometric games to give an impression of 3D. After the grass is drawn, we draw some trees at some predefined positions on the screen. As you can see, manipulating the object contained in the smart pointer is completely transparent (it is as if it were manipulating the object directly). We finally draw the knight sprite and switch to the next frame in the animation. Moving the knight sprite is done in the
Update
function:void CMainWindow::Update(DWORD dwCurrentTime){ int xOffset = 0; int yOffset = 0; if (m_KeysDown[0]) yOffset -= 5; if (m_KeysDown[1]) yOffset += 5; if (m_KeysDown[2]) xOffset -= 5; if (m_KeysDown[3]) xOffset += 5; m_pKnightSprite->OffsetPosition(xOffset, yOffset);}
If one of the keys is pressed, we move the sprite by a certain offset. As the time is passed to the function, we could also calculate the offset to apply to the sprite depending on the time elapsed. So, you are now ready to test the example and move your knight on the screen. Of course, the scene should probably be loaded from a file that is generated from a specific editor, but that falls outside the scope of this article.
- Part2:Example
- part2
- PART2
- example
- example
- Example
- example
- Example
- Example
- Example
- Example
- example
- Example
- Example
- SyntaxHighlighter part2
- Mediation part2
- MileStone(part2)
- HTRC110 part2
- javaSE 4.2
- 结构体中定义的联合成员可以直接访问!
- notepad 列选择
- 为ubuntu默认输入法ibus设置翻页快捷键
- C语言运算符优先级 详细列表
- Part2:Example
- iphone——日期处理
- shell中取SVN代码的版本号
- 随手记一记学Perl 时容易搞混的地方
- 【五一呈献】通用高性能 Windows Socket 组件 HP-Socket v2.1.1 正式发布
- SDRAM讲解
- VS2008 在h与cpp文件间切换快捷键
- bind delegate live的区别。在jquery中
- 求sn=a+aa+aaa+aaa......+aa...a(n个a)之值,其中a是一个数字,例如:2+22+222+2222+22222(此时n=5),n由键盘输入