子画面管理器集成在游戏引擎类里面,对子画面对象进行一些操作处理,例如把子画面集中在一个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"
程序截图:

本文介绍了一种游戏引擎中子画面对象的管理方法,通过使用vector容器来存储和操作子画面对象,实现了批量更新和绘制的功能。此外,还介绍了如何解决游戏画面闪烁问题,采用了双重缓存技术。
420

被折叠的 条评论
为什么被折叠?



