子画面管理器集成在游戏引擎类里面,对子画面对象进行一些操作处理,例如把子画面集中在一个vector类中,对其进行批量更新和绘制。因为修改的东西较多,我也懒得写了,贴下代码吧。另外此次修改也顺便解决下游戏画面的闪烁问题,自然采用双重缓存技术。
代码清单:
//----------------------------------------------------------------- // Game Engine Object // C++ Header - GameEngine.h //----------------------------------------------------------------- #pragma once //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include #include #include using namespace std; #include "Sprite.h" //----------------------------------------------------------------- // Joystick Flags //----------------------------------------------------------------- typedef WORD JOYSTATE; const JOYSTATE JOY_NONE = 0x0000L, JOY_LEFT = 0x0001L, JOY_RIGHT = 0x0002L, JOY_UP = 0x0004L, JOY_DOWN = 0x0008L, JOY_FIRE1 = 0x0010L, JOY_FIRE2 = 0x0020L; //----------------------------------------------------------------- // Windows Function Declarations //----------------------------------------------------------------- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); //----------------------------------------------------------------- // Game Engine Function Declarations //----------------------------------------------------------------- BOOL GameInitialize(HINSTANCE hInstance); void GameStart(HWND hWindow); void GameEnd(); void GameActivate(HWND hWindow); void GameDeactivate(HWND hWindow); void GamePaint(HDC hDC); void GameCycle(); void HandleKeys(); void MouseButtonDown(int x, int y, BOOL bLeft); void MouseButtonUp(int x, int y, BOOL bLeft); void MouseMove(int x, int y); void HandleJoystick(JOYSTATE jsJoystickState); BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee); //新增的游戏方法,定义了游戏对碰撞的处理 //----------------------------------------------------------------- // GameEngine Class //----------------------------------------------------------------- class GameEngine { protected: // Member Variables static GameEngine* m_pGameEngine; HINSTANCE m_hInstance; HWND m_hWindow; TCHAR m_szWindowClass[32]; TCHAR m_szTitle[32]; WORD m_wIcon, m_wSmallIcon; int m_iWidth, m_iHeight; int m_iFrameDelay; BOOL m_bSleep; UINT m_uiJoystickID; RECT m_rcJoystickTrip; vector m_vSprites; //增加了一个vector类,记录子画面列表 // Helper Methods BOOL CheckSpriteCollision(Sprite* pTestSprite); public: // Constructor(s)/Destructor GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480); virtual ~GameEngine(); // General Methods static GameEngine* GetEngine() { return m_pGameEngine; }; BOOL Initialize(int iCmdShow); LRESULT HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam); void ErrorQuit(LPTSTR szErrorMsg); BOOL InitJoystick(); void CaptureJoystick(); void ReleaseJoystick(); void CheckJoystick(); //以下常规方法为子画面管理器的一组方法 void AddSprite(Sprite* pSprite); void DrawSprites(HDC hDC); void UpdateSprites(); void CleanupSprites(); Sprite* IsPointInSprite(int x, int y); // Accessor Methods HINSTANCE GetInstance() { return m_hInstance; }; HWND GetWindow() { return m_hWindow; }; void SetWindow(HWND hWindow) { m_hWindow = hWindow; }; LPTSTR GetTitle() { return m_szTitle; }; WORD GetIcon() { return m_wIcon; }; WORD GetSmallIcon() { return m_wSmallIcon; }; int GetWidth() { return m_iWidth; }; int GetHeight() { return m_iHeight; }; int GetFrameDelay() { return m_iFrameDelay; }; void SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 / iFrameRate; }; BOOL GetSleep() { return m_bSleep; }; void SetSleep(BOOL bSleep) { m_bSleep = bSleep; }; };
//----------------------------------------------------------------- // Game Engine Object // C++ Source - GameEngine.cpp //----------------------------------------------------------------- //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include "GameEngine.h" //----------------------------------------------------------------- // Static Variable Initialization //----------------------------------------------------------------- GameEngine *GameEngine::m_pGameEngine = NULL; //----------------------------------------------------------------- // Windows Functions //----------------------------------------------------------------- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg; static int iTickTrigger = 0; int iTickCount; if (GameInitialize(hInstance)) { // Initialize the game engine if (!GameEngine::GetEngine()->Initialize(iCmdShow)) return FALSE; // Enter the main message loop while (TRUE) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Process the message if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { // Make sure the game engine isn't sleeping if (!GameEngine::GetEngine()->GetSleep()) { // Check the tick count to see if a game cycle has elapsed iTickCount = GetTickCount(); if (iTickCount > iTickTrigger) { iTickTrigger = iTickCount + GameEngine::GetEngine()->GetFrameDelay(); HandleKeys(); GameEngine::GetEngine()->CheckJoystick(); GameCycle(); } } } } return (int)msg.wParam; } // End the game GameEnd(); return TRUE; } LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam) { // Route all Windows messages to the game engine return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam); } //----------------------------------------------------------------- // Game Engine Helper Methods //----------------------------------------------------------------- BOOL GameEngine::CheckSpriteCollision(Sprite* pTestSprite) { // See if the sprite has collided with any other sprites vector ::iterator siSprite; for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++) { // Make sure not to check for collision with itself if (pTestSprite == (*siSprite)) continue; // Test the collision if (pTestSprite->TestCollision(*siSprite)) // Collision detected return SpriteCollision((*siSprite), pTestSprite); } // No collision return FALSE; } //----------------------------------------------------------------- // GameEngine Constructor(s)/Destructor //----------------------------------------------------------------- GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight) { // Set the member variables for the game engine m_pGameEngine = this; m_hInstance = hInstance; m_hWindow = NULL; if (lstrlen(szWindowClass) > 0) lstrcpy(m_szWindowClass, szWindowClass); if (lstrlen(szTitle) > 0) lstrcpy(m_szTitle, szTitle); m_wIcon = wIcon; m_wSmallIcon = wSmallIcon; m_iWidth = iWidth; m_iHeight = iHeight; m_iFrameDelay = 50; // 20 FPS default m_bSleep = TRUE; m_uiJoystickID = 0; m_vSprites.reserve(100); //可以容纳100个子画面指针对象 } GameEngine::~GameEngine() { } //----------------------------------------------------------------- // Game Engine General Methods //----------------------------------------------------------------- BOOL GameEngine::Initialize(int iCmdShow) { WNDCLASSEX wndclass; // Create the window class for the main window wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = m_hInstance; wndclass.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetIcon())); wndclass.hIconSm = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetSmallIcon())); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = m_szWindowClass; // Register the window class if (!RegisterClassEx(&wndclass)) return FALSE; // Calculate the window size and position based upon the game size int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2, iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION); if (wndclass.lpszMenuName != NULL) iWindowHeight += GetSystemMetrics(SM_CYMENU); int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2, iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2; // Create the window m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth, iWindowHeight, NULL, NULL, m_hInstance, NULL); if (!m_hWindow) return FALSE; // Show and update the window ShowWindow(m_hWindow, iCmdShow); UpdateWindow(m_hWindow); return TRUE; } LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam) { // Route Windows messages to game engine member functions switch (msg) { case WM_CREATE: // Set the game window and start the game SetWindow(hWindow); GameStart(hWindow); return 0; case WM_SETFOCUS: // Activate the game and update the Sleep status GameActivate(hWindow); SetSleep(FALSE); return 0; case WM_KILLFOCUS: // Deactivate the game and update the Sleep status GameDeactivate(hWindow); SetSleep(TRUE); return 0; case WM_PAINT: HDC hDC; PAINTSTRUCT ps; hDC = BeginPaint(hWindow, &ps); // Paint the game GamePaint(hDC); EndPaint(hWindow, &ps); return 0; case WM_LBUTTONDOWN: // Handle left mouse button press MouseButtonDown(LOWORD(lParam), HIWORD(lParam), TRUE); return 0; case WM_LBUTTONUP: // Handle left mouse button release MouseButtonUp(LOWORD(lParam), HIWORD(lParam), TRUE); return 0; case WM_RBUTTONDOWN: // Handle right mouse button press MouseButtonDown(LOWORD(lParam), HIWORD(lParam), FALSE); return 0; case WM_RBUTTONUP: // Handle right mouse button release MouseButtonUp(LOWORD(lParam), HIWORD(lParam), FALSE); return 0; case WM_MOUSEMOVE: // Handle mouse movement MouseMove(LOWORD(lParam), HIWORD(lParam)); return 0; case WM_DESTROY: // End the game and exit the application GameEnd(); PostQuitMessage(0); return 0; } return DefWindowProc(hWindow, msg, wParam, lParam); } void GameEngine::ErrorQuit(LPTSTR szErrorMsg) { MessageBox(GetWindow(), szErrorMsg, TEXT("Critical Error"), MB_OK | MB_ICONERROR); PostQuitMessage(0); } BOOL GameEngine::InitJoystick() { // Make sure joystick driver is present UINT uiNumJoysticks; if ((uiNumJoysticks = joyGetNumDevs()) == 0) return FALSE; // Make sure the joystick is attached JOYINFO jiInfo; if (joyGetPos(JOYSTICKID1, &jiInfo) != JOYERR_UNPLUGGED) m_uiJoystickID = JOYSTICKID1; else return FALSE; // Calculate the trip values JOYCAPS jcCaps; joyGetDevCaps(m_uiJoystickID, &jcCaps, sizeof(JOYCAPS)); DWORD dwXCenter = ((DWORD)jcCaps.wXmin + jcCaps.wXmax) / 2; DWORD dwYCenter = ((DWORD)jcCaps.wYmin + jcCaps.wYmax) / 2; m_rcJoystickTrip.left = (jcCaps.wXmin + (WORD)dwXCenter) / 2; m_rcJoystickTrip.right = (jcCaps.wXmax + (WORD)dwXCenter) / 2; m_rcJoystickTrip.top = (jcCaps.wYmin + (WORD)dwYCenter) / 2; m_rcJoystickTrip.bottom = (jcCaps.wYmax + (WORD)dwYCenter) / 2; return TRUE; } void GameEngine::CaptureJoystick() { // Capture the joystick if (m_uiJoystickID == JOYSTICKID1) joySetCapture(m_hWindow, m_uiJoystickID, NULL, TRUE); } void GameEngine::ReleaseJoystick() { // Release the joystick if (m_uiJoystickID == JOYSTICKID1) joyReleaseCapture(m_uiJoystickID); } void GameEngine::CheckJoystick() { if (m_uiJoystickID == JOYSTICKID1) { JOYINFO jiInfo; JOYSTATE jsJoystickState = 0; if (joyGetPos(m_uiJoystickID, &jiInfo) == JOYERR_NOERROR) { // Check horizontal movement if (jiInfo.wXpos < (WORD)m_rcJoystickTrip.left) jsJoystickState |= JOY_LEFT; else if (jiInfo.wXpos > (WORD)m_rcJoystickTrip.right) jsJoystickState |= JOY_RIGHT; // Check vertical movement if (jiInfo.wYpos < (WORD)m_rcJoystickTrip.top) jsJoystickState |= JOY_UP; else if (jiInfo.wYpos > (WORD)m_rcJoystickTrip.bottom) jsJoystickState |= JOY_DOWN; // Check buttons if(jiInfo.wButtons & JOY_BUTTON1) jsJoystickState |= JOY_FIRE1; if(jiInfo.wButtons & JOY_BUTTON2) jsJoystickState |= JOY_FIRE2; } // Allow the game to handle the joystick HandleJoystick(jsJoystickState); } } void GameEngine::AddSprite(Sprite* pSprite) { // 向子画面列表增加一个子画面对象 if (pSprite != NULL) { // See if there are sprites already in the sprite vector if (m_vSprites.size() > 0) { // Find a spot in the sprite vector to insert the sprite by its z-order vector ::iterator siSprite; for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++) if (pSprite->GetZOrder() < (*siSprite)->GetZOrder()) { // Insert the sprite into the sprite vector m_vSprites.insert(siSprite, pSprite); return; } } // The sprite's z-order is highest, so add it to the end of the vector m_vSprites.push_back(pSprite); } } void GameEngine::DrawSprites(HDC hDC) { // Draw the sprites in the sprite vector vector ::iterator siSprite; for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++) (*siSprite)->Draw(hDC); } void GameEngine::UpdateSprites() { // Check to see if the sprite vector needs to grow if (m_vSprites.size() >= (m_vSprites.capacity() / 2)) m_vSprites.reserve(m_vSprites.capacity() * 2); // Update the sprites in the sprite vector RECT rcOldSpritePos; SPRITEACTION saSpriteAction; vector ::iterator siSprite; for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++) { // Save the old sprite position in case we need to restore it rcOldSpritePos = (*siSprite)->GetPosition(); // Update the sprite saSpriteAction = (*siSprite)->Update(); // Handle the SA_KILL sprite action if (saSpriteAction & SA_KILL) { delete (*siSprite); m_vSprites.erase(siSprite); siSprite--; continue; } // See if the sprite collided with any others if (CheckSpriteCollision(*siSprite)) // Restore the old sprite position (*siSprite)->SetPosition(rcOldSpritePos); } } void GameEngine::CleanupSprites() { // Delete and remove the sprites in the sprite vector vector ::iterator siSprite; for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++) { delete (*siSprite); m_vSprites.erase(siSprite); siSprite--; } } Sprite* GameEngine::IsPointInSprite(int x, int y) { // See if the point is in a sprite in the sprite vector vector ::reverse_iterator siSprite; for (siSprite = m_vSprites.rbegin(); siSprite != m_vSprites.rend(); siSprite++) if (!(*siSprite)->IsHidden() && (*siSprite)->IsPointInside(x, y)) return (*siSprite); // The point is not in a sprite return NULL; }
//----------------------------------------------------------------- // Sprite Object // C++ Header - Sprite.h //----------------------------------------------------------------- #pragma once //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include #include "Bitmap.h" //----------------------------------------------------------------- // Custom Data Types //----------------------------------------------------------------- typedef WORD SPRITEACTION; //新增结构类型,定义子画面行为方式 const SPRITEACTION SA_NONE = 0x0000L, SA_KILL = 0x0001L; typedef WORD BOUNDSACTION; const BOUNDSACTION BA_STOP = 0, BA_WRAP = 1, BA_BOUNCE = 2, BA_DIE = 3; //----------------------------------------------------------------- // Sprite Class //----------------------------------------------------------------- class Sprite { protected: // Member Variables Bitmap* m_pBitmap; RECT m_rcPosition, m_rcCollision; //新增成员变量,碰撞矩形 POINT m_ptVelocity; int m_iZOrder; RECT m_rcBounds; BOUNDSACTION m_baBoundsAction; BOOL m_bHidden; // Helper Methods virtual void CalcCollisionRect(); public: // Constructor(s)/Destructor Sprite(Bitmap* pBitmap); Sprite(Bitmap* pBitmap, RECT& rcBounds, BOUNDSACTION baBoundsAction = BA_STOP); Sprite(Bitmap* pBitmap, POINT ptPosition, POINT ptVelocity, int iZOrder, RECT& rcBounds, BOUNDSACTION baBoundsAction = BA_STOP); virtual ~Sprite(); // General Methods virtual SPRITEACTION Update(); void Draw(HDC hDC); BOOL IsPointInside(int x, int y); BOOL TestCollision(Sprite* pTestSprite); // Accessor Methods RECT& GetPosition() { return m_rcPosition; }; void SetPosition(int x, int y); void SetPosition(POINT ptPosition); void SetPosition(RECT& rcPosition) { CopyRect(&m_rcPosition, &rcPosition); }; void OffsetPosition(int x, int y); RECT& GetCollision() { return m_rcCollision; }; POINT GetVelocity() { return m_ptVelocity; }; void SetVelocity(int x, int y); void SetVelocity(POINT ptVelocity); BOOL GetZOrder() { return m_iZOrder; }; void SetZOrder(int iZOrder) { m_iZOrder = iZOrder; }; void SetBounds(RECT& rcBounds) { CopyRect(&m_rcBounds, &rcBounds); }; void SetBoundsAction(BOUNDSACTION ba) { m_baBoundsAction = ba; }; BOOL IsHidden() { return m_bHidden; }; void SetHidden(BOOL bHidden) { m_bHidden = bHidden; }; int GetWidth() { return m_pBitmap->GetWidth(); }; int GetHeight() { return m_pBitmap->GetHeight(); }; }; //----------------------------------------------------------------- // Sprite Inline Helper Methods //----------------------------------------------------------------- inline void Sprite::CalcCollisionRect() { int iXShrink = (m_rcPosition.left - m_rcPosition.right) / 12; int iYShrink = (m_rcPosition.top - m_rcPosition.bottom) / 12; CopyRect(&m_rcCollision, &m_rcPosition); InflateRect(&m_rcCollision, iXShrink, iYShrink); } //----------------------------------------------------------------- // Sprite Inline General Methods //----------------------------------------------------------------- inline BOOL Sprite::TestCollision(Sprite* pTestSprite) { RECT& rcTest = pTestSprite->GetCollision(); return m_rcCollision.left <= rcTest.right && rcTest.left <= m_rcCollision.right && m_rcCollision.top <= rcTest.bottom && rcTest.top <= m_rcCollision.bottom; } inline BOOL Sprite::IsPointInside(int x, int y) { POINT ptPoint; ptPoint.x = x; ptPoint.y = y; return PtInRect(&m_rcPosition, ptPoint); } //----------------------------------------------------------------- // Sprite Inline Accessor Methods //----------------------------------------------------------------- inline void Sprite::SetPosition(int x, int y) { OffsetRect(&m_rcPosition, x - m_rcPosition.left, y - m_rcPosition.top); CalcCollisionRect(); } inline void Sprite::SetPosition(POINT ptPosition) { OffsetRect(&m_rcPosition, ptPosition.x - m_rcPosition.left, ptPosition.y - m_rcPosition.top); CalcCollisionRect(); } inline void Sprite::OffsetPosition(int x, int y) { OffsetRect(&m_rcPosition, x, y); CalcCollisionRect(); } inline void Sprite::SetVelocity(int x, int y) { m_ptVelocity.x = x; m_ptVelocity.y = y; } inline void Sprite::SetVelocity(POINT ptVelocity) { m_ptVelocity.x = ptVelocity.x; m_ptVelocity.y = ptVelocity.y; }
//----------------------------------------------------------------- // Sprite Object // C++ Source - Sprite.cpp //----------------------------------------------------------------- //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include "Sprite.h" //----------------------------------------------------------------- // Sprite Constructor(s)/Destructor //----------------------------------------------------------------- Sprite::Sprite(Bitmap* pBitmap) { // Initialize the member variables m_pBitmap = pBitmap; SetRect(&m_rcPosition, 0, 0, pBitmap->GetWidth(), pBitmap->GetHeight()); CalcCollisionRect(); m_ptVelocity.x = m_ptVelocity.y = 0; m_iZOrder = 0; SetRect(&m_rcBounds, 0, 0, 640, 480); m_baBoundsAction = BA_STOP; m_bHidden = FALSE; } Sprite::Sprite(Bitmap* pBitmap, RECT& rcBounds, BOUNDSACTION baBoundsAction) { // Calculate a random position int iXPos = rand() % (rcBounds.right - rcBounds.left); int iYPos = rand() % (rcBounds.bottom - rcBounds.top); // Initialize the member variables m_pBitmap = pBitmap; SetRect(&m_rcPosition, iXPos, iYPos, iXPos + pBitmap->GetWidth(), iYPos + pBitmap->GetHeight()); CalcCollisionRect(); m_ptVelocity.x = m_ptVelocity.y = 0; m_iZOrder = 0; CopyRect(&m_rcBounds, &rcBounds); m_baBoundsAction = baBoundsAction; m_bHidden = FALSE; } Sprite::Sprite(Bitmap* pBitmap, POINT ptPosition, POINT ptVelocity, int iZOrder, RECT& rcBounds, BOUNDSACTION baBoundsAction) { // Initialize the member variables m_pBitmap = pBitmap; SetRect(&m_rcPosition, ptPosition.x, ptPosition.y, ptPosition.x + pBitmap->GetWidth(), ptPosition.y + pBitmap->GetHeight()); CalcCollisionRect(); m_ptVelocity = ptVelocity; m_iZOrder = iZOrder; CopyRect(&m_rcBounds, &rcBounds); m_baBoundsAction = baBoundsAction; m_bHidden = FALSE; } Sprite::~Sprite() { } //----------------------------------------------------------------- // Sprite General Methods //----------------------------------------------------------------- SPRITEACTION Sprite::Update() { // Update the position POINT ptNewPosition, ptSpriteSize, ptBoundsSize; ptNewPosition.x = m_rcPosition.left + m_ptVelocity.x; ptNewPosition.y = m_rcPosition.top + m_ptVelocity.y; ptSpriteSize.x = m_rcPosition.right - m_rcPosition.left; ptSpriteSize.y = m_rcPosition.bottom - m_rcPosition.top; ptBoundsSize.x = m_rcBounds.right - m_rcBounds.left; ptBoundsSize.y = m_rcBounds.bottom - m_rcBounds.top; // Check the bounds // Wrap? if (m_baBoundsAction == BA_WRAP) { if ((ptNewPosition.x + ptSpriteSize.x) < m_rcBounds.left) ptNewPosition.x = m_rcBounds.right; else if (ptNewPosition.x > m_rcBounds.right) ptNewPosition.x = m_rcBounds.left - ptSpriteSize.x; if ((ptNewPosition.y + ptSpriteSize.y) < m_rcBounds.top) ptNewPosition.y = m_rcBounds.bottom; else if (ptNewPosition.y > m_rcBounds.bottom) ptNewPosition.y = m_rcBounds.top - ptSpriteSize.y; } // Bounce? else if (m_baBoundsAction == BA_BOUNCE) { BOOL bBounce = FALSE; POINT ptNewVelocity = m_ptVelocity; if (ptNewPosition.x < m_rcBounds.left) { bBounce = TRUE; ptNewPosition.x = m_rcBounds.left; ptNewVelocity.x = -ptNewVelocity.x; } else if ((ptNewPosition.x + ptSpriteSize.x) > m_rcBounds.right) { bBounce = TRUE; ptNewPosition.x = m_rcBounds.right - ptSpriteSize.x; ptNewVelocity.x = -ptNewVelocity.x; } if (ptNewPosition.y < m_rcBounds.top) { bBounce = TRUE; ptNewPosition.y = m_rcBounds.top; ptNewVelocity.y = -ptNewVelocity.y; } else if ((ptNewPosition.y + ptSpriteSize.y) > m_rcBounds.bottom) { bBounce = TRUE; ptNewPosition.y = m_rcBounds.bottom - ptSpriteSize.y; ptNewVelocity.y = -ptNewVelocity.y; } if (bBounce) SetVelocity(ptNewVelocity); } // Die? else if (m_baBoundsAction == BA_DIE) { if ((ptNewPosition.x + ptSpriteSize.x) < m_rcBounds.left || ptNewPosition.x > m_rcBounds.right || (ptNewPosition.y + ptSpriteSize.y) < m_rcBounds.top || ptNewPosition.y > m_rcBounds.bottom) return SA_KILL; } // Stop (default) else { if (ptNewPosition.x < m_rcBounds.left || ptNewPosition.x > (m_rcBounds.right - ptSpriteSize.x)) { ptNewPosition.x = max(m_rcBounds.left, min(ptNewPosition.x, m_rcBounds.right - ptSpriteSize.x)); SetVelocity(0, 0); } if (ptNewPosition.y < m_rcBounds.top || ptNewPosition.y > (m_rcBounds.bottom - ptSpriteSize.y)) { ptNewPosition.y = max(m_rcBounds.top, min(ptNewPosition.y, m_rcBounds.bottom - ptSpriteSize.y)); SetVelocity(0, 0); } } SetPosition(ptNewPosition); return SA_NONE; } void Sprite::Draw(HDC hDC) { // Draw the sprite if it isn't hidden if (m_pBitmap != NULL && !m_bHidden) m_pBitmap->Draw(hDC, m_rcPosition.left, m_rcPosition.top, TRUE); }
位图类没有什么改动,基于以上修改,一个相应的实例程序如下。
//----------------------------------------------------------------- // Planets Application // C++ Header - Planets.h //----------------------------------------------------------------- #pragma once //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include #include "Resource.h" #include "GameEngine.h" #include "Bitmap.h" #include "Sprite.h" //----------------------------------------------------------------- // Global Variables //----------------------------------------------------------------- HINSTANCE g_hInstance; GameEngine* g_pGame; HDC g_hOffscreenDC; //内存设备环境 HBITMAP g_hOffscreenBitmap; //屏幕外位图 Bitmap* g_pGalaxyBitmap; Bitmap* g_pPlanetBitmap[3]; Sprite* g_pDragSprite;
//----------------------------------------------------------------- // Planets 2 Application // C++ Source - Planets.cpp //----------------------------------------------------------------- //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include "Planets.h" //----------------------------------------------------------------- // Game Engine Functions //----------------------------------------------------------------- BOOL GameInitialize(HINSTANCE hInstance) { // Create the game engine g_pGame = new GameEngine(hInstance, TEXT("Planets 2"), TEXT("Planets 2"), IDI_PLANETS, IDI_PLANETS_SM, 600, 400); if (g_pGame == NULL) return FALSE; // Set the frame rate g_pGame->SetFrameRate(30); // Store the instance handle g_hInstance = hInstance; return TRUE; } void GameStart(HWND hWindow) { // Seed the random number generator srand(GetTickCount()); // 创建屏幕外设备环境和位图 g_hOffscreenDC = CreateCompatibleDC(GetDC(hWindow)); g_hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWindow), g_pGame->GetWidth(), g_pGame->GetHeight()); SelectObject(g_hOffscreenDC, g_hOffscreenBitmap); // Create and load the bitmaps HDC hDC = GetDC(hWindow); g_pGalaxyBitmap = new Bitmap(hDC, IDB_GALAXY, g_hInstance); g_pPlanetBitmap[0] = new Bitmap(hDC, IDB_PLANET1, g_hInstance); g_pPlanetBitmap[1] = new Bitmap(hDC, IDB_PLANET2, g_hInstance); g_pPlanetBitmap[2] = new Bitmap(hDC, IDB_PLANET3, g_hInstance); // 创建行星子画面 RECT rcBounds = { 0, 0, 600, 400 }; Sprite* pSprite; pSprite = new Sprite(g_pPlanetBitmap[0], rcBounds, BA_WRAP); pSprite->SetVelocity(3, 2); g_pGame->AddSprite(pSprite); pSprite = new Sprite(g_pPlanetBitmap[1], rcBounds, BA_WRAP); pSprite->SetVelocity(4, 1); g_pGame->AddSprite(pSprite); rcBounds.right = 200; rcBounds.bottom = 160; pSprite = new Sprite(g_pPlanetBitmap[2], rcBounds, BA_BOUNCE); pSprite->SetVelocity(-4, 2); g_pGame->AddSprite(pSprite); rcBounds.left = 400; rcBounds.top = 240; rcBounds.right = 600; rcBounds.bottom = 400; pSprite = new Sprite(g_pPlanetBitmap[2], rcBounds, BA_BOUNCE); pSprite->SetVelocity(7, -3); g_pGame->AddSprite(pSprite); // 设置初始拖动信息 g_pDragSprite = NULL; } void GameEnd() { // Cleanup the offscreen device context and bitmap DeleteObject(g_hOffscreenBitmap); DeleteDC(g_hOffscreenDC); // Cleanup the bitmaps delete g_pGalaxyBitmap; for (int i = 0; i < 3; i++) delete g_pPlanetBitmap[i]; // Cleanup the sprites g_pGame->CleanupSprites(); // Cleanup the game engine delete g_pGame; } void GameActivate(HWND hWindow) { } void GameDeactivate(HWND hWindow) { } void GamePaint(HDC hDC) { // Draw the background galaxy g_pGalaxyBitmap->Draw(hDC, 0, 0); // Draw the sprites g_pGame->DrawSprites(hDC); } void GameCycle() { // Update the sprites g_pGame->UpdateSprites(); // Obtain a device context for repainting the game HWND hWindow = g_pGame->GetWindow(); HDC hDC = GetDC(hWindow); // Paint the game to the offscreen device context GamePaint(g_hOffscreenDC); // Blit the offscreen bitmap to the game screen BitBlt(hDC, 0, 0, g_pGame->GetWidth(), g_pGame->GetHeight(), g_hOffscreenDC, 0, 0, SRCCOPY); // Cleanup ReleaseDC(hWindow, hDC); } void HandleKeys() { } void MouseButtonDown(int x, int y, BOOL bLeft) { // See if a planet was clicked with the left mouse button if (bLeft && (g_pDragSprite == NULL)) { if ((g_pDragSprite = g_pGame->IsPointInSprite(x, y)) != NULL) { // Capture the mouse SetCapture(g_pGame->GetWindow()); // Simulate a mouse move to get started MouseMove(x, y); } } } void MouseButtonUp(int x, int y, BOOL bLeft) { // Release the mouse ReleaseCapture(); // Stop dragging g_pDragSprite = NULL; } void MouseMove(int x, int y) { if (g_pDragSprite != NULL) { // Move the sprite to the mouse cursor position g_pDragSprite->SetPosition(x - (g_pDragSprite->GetWidth() / 2), y - (g_pDragSprite->GetHeight() / 2)); // Force a repaint to redraw the sprites InvalidateRect(g_pGame->GetWindow(), NULL, FALSE); } } void HandleJoystick(JOYSTATE jsJoystickState) { } BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee) { // Swap the sprite velocities so that they appear to bounce POINT ptSwapVelocity = pSpriteHitter->GetVelocity(); pSpriteHitter->SetVelocity(pSpriteHittee->GetVelocity()); pSpriteHittee->SetVelocity(ptSwapVelocity); return TRUE; }
//----------------------------------------------------------------- // Planets Resource Identifiers // C++ Header - Resource.h //----------------------------------------------------------------- //----------------------------------------------------------------- // Icons Range : 1000 - 1999 //----------------------------------------------------------------- #define IDI_PLANETS 1000 #define IDI_PLANETS_SM 1001 //----------------------------------------------------------------- // Bitmaps Range : 2000 - 2999 //----------------------------------------------------------------- #define IDB_GALAXY 2000 #define IDB_PLANET1 2001 #define IDB_PLANET2 2002 #define IDB_PLANET3 2003
//----------------------------------------------------------------- // Planets Resources // RC Source - Planets.rc //----------------------------------------------------------------- //----------------------------------------------------------------- // Include Files //----------------------------------------------------------------- #include "Resource.h" //----------------------------------------------------------------- // Icons //----------------------------------------------------------------- IDI_PLANETS ICON "Res//Planets.ico" IDI_PLANETS_SM ICON "Res//Planets_sm.ico" //----------------------------------------------------------------- // Bitmaps //----------------------------------------------------------------- IDB_GALAXY BITMAP "Res//Galaxy.bmp" IDB_PLANET1 BITMAP "Res//Planet1.bmp" IDB_PLANET2 BITMAP "Res//Planet2.bmp" IDB_PLANET3 BITMAP "Res//Planet3.bmp"
程序截图: