|
Documentation
|
Creating a complete game: BreakoutThis document has been updated for use with GapiDraw 4.0 or later. |
Step 7 : BricksWe will now add bricks to the game. We will do this using a new structure called BREAKBRICK, and we will also calculate the number of bricks on the game board dynamically - so that the entire display is filled with bricks.
First, download the file bricks.png and save it to your Breakout\Common\res folder. Then add it to your Visual Studio project as a PNG image and name it IDB_BRICKS. Add the following code to myapplication.h: //----------------------------------------------------------------------------- // Name: struct CBreakBrick // Desc: Everything related to bricks //----------------------------------------------------------------------------- typedef struct _BREAKBRICK { BOOL bShowBrick; DWORD dwBrickIndex; DWORD dwShineIndex; DWORD dwExplosionIndex; int nExplosionCenterX; int nExplosionCenterY; } BREAKBRICK; class CMyApplication : public CGapiApplication { ... BREAKBRICK* m_pBricks; CGapiSurface* m_pBrickSurface; DWORD m_dwGameBricksPerLine; ... } Add the following code to myapplication.cpp: CMyApplication::CMyApplication(const GDAPPCONFIG& config) : CGapiApplication(config) { ... m_pBricks = NULL; m_pBrickSurface = new CGapiSurface(GetGlobal()); m_dwGameBricksPerLine = 0; ... } CMyApplication::~CMyApplication() { ... if (m_pBricks) delete m_pBricks; delete m_pBrickSurface; ... } HRESULT CMyApplication::CreateVidMemSurfaces(CGapiSurface* pBackBuffer, HINSTANCE hInstance) { ... m_pBrickSurface->CreateSurface(0, hInstance, IDB_BRICKS, _T("PNG")); ... } HRESULT CMyApplication::GameCheckBricks(CGapiSurface* pBackBuffer) { int nScreenWidth = pBackBuffer->GetWidth(); int nScreenHeight = pBackBuffer->GetHeight(); int nScreenXCenter = nScreenWidth >> 1; int nScreenYCenter = nScreenHeight >> 1; int nBallX = m_pBall->GetScreenX(); int nBallY = m_pBall->GetScreenY(); int nBallWidth = m_pBall->GetWidth(); int nBallHeight = m_pBall->GetHeight(); // Check to see if the ball is outside brick area if (nBallY < (GAMEPARAM_BRICKOFFSETY - nBallHeight) || nBallY > (GAMEPARAM_BRICKOFFSETY + GAMEPARAM_NUMLINES * (GAMEPARAM_BRICKSPACE + GAMEPARAM_BRICKHEIGHT))) { return S_OK; } // Check for brick collision int nBrickOffsetX = (nScreenWidth - 2 * GAMEPARAM_BORDERWIDTH - m_dwGameBricksPerLine * (GAMEPARAM_BRICKWIDTH + GAMEPARAM_BRICKSPACE)) >> 1; for (int nRowY = 0; nRowY < GAMEPARAM_NUMLINES; nRowY++) { for (int nRowX = 0; nRowX < (int)m_dwGameBricksPerLine; nRowX++) { if (m_pBricks[nRowY * (int) m_dwGameBricksPerLine + nRowX].bShowBrick) { int nY = GAMEPARAM_BRICKOFFSETY + nRowY * (GAMEPARAM_BRICKSPACE + GAMEPARAM_BRICKHEIGHT); int nX = GAMEPARAM_BORDERWIDTH + nBrickOffsetX + nRowX * (GAMEPARAM_BRICKSPACE + GAMEPARAM_BRICKWIDTH); if (nBallY >= (nY - nBallHeight) && nBallY < (nY + GAMEPARAM_BRICKHEIGHT) && nBallX >= (nX - nBallWidth) && nBallX < (nX + GAMEPARAM_BRICKWIDTH)) { // Remove brick and trigger an explosion int nBrickIndex = nRowY * m_dwGameBricksPerLine + nRowX; m_pBricks[nBrickIndex].bShowBrick = FALSE; m_pBricks[nBrickIndex].dwShineIndex = 0; m_pBricks[nBrickIndex].dwExplosionIndex = GAMEPARAM_NUMEXPLOSIONS; m_pBricks[nBrickIndex].nExplosionCenterX = nBallX + (nBallWidth >> 1); m_pBricks[nBrickIndex].nExplosionCenterY = nBallY + (nBallHeight >> 1); // Calculate Bounce left and right - remember that ball moves in subpixel increments if ((nBallX <= (nX - nBallWidth + 1)) || (nBallX >= (nX + GAMEPARAM_BRICKWIDTH - 1))) { m_pBall->InvertXSpeed(); } // Calculate Bounce top and bottom - remember that ball moves in subpixel increments if ((nBallY <= (nY - nBallHeight + 1)) || (nBallY >= (nY + GAMEPARAM_BRICKHEIGHT - 1))) { m_pBall->InvertYSpeed(); } // Finally award points for the brick if (m_dwGameMode == GAMEMODE_INGAME) { m_dwPlayerScore += (GAMEPARAM_NUMLINES - nRowY); } // Only break one brick at the time return S_OK; } } } } return S_OK; } HRESULT CMyApplication::GameDrawObjects(CGapiSurface* pBackBuffer) { // Bricks BOOL bLevelClear = TRUE; DWORD dwColorShift = 255/(GAMEPARAM_NUMLINES); DWORD dwBrickOffsetX = (nScreenWidth - 2 * GAMEPARAM_BORDERWIDTH - m_dwGameBricksPerLine * (GAMEPARAM_BRICKWIDTH + GAMEPARAM_BRICKSPACE)) >> 1; GDFILLRECTFX fillfx; fillfx.dwOpacity = 64; DWORD dwX, dwY; for (dwY=0; dwY < GAMEPARAM_NUMLINES; dwY++) { for (dwX=0; dwX < m_dwGameBricksPerLine; dwX++) { DWORD dwBrickIndex = dwY * m_dwGameBricksPerLine + dwX; if (m_pBricks[dwBrickIndex].bShowBrick) { bLevelClear = FALSE; DWORD dwLeft = GAMEPARAM_BORDERWIDTH + dwBrickOffsetX + dwX * (GAMEPARAM_BRICKWIDTH + GAMEPARAM_BRICKSPACE); DWORD dwTop = GAMEPARAM_BRICKOFFSETY + dwY * (GAMEPARAM_BRICKHEIGHT + GAMEPARAM_BRICKSPACE); DWORD dwRight = dwLeft + GAMEPARAM_BRICKWIDTH; DWORD dwBottom = dwTop + GAMEPARAM_BRICKHEIGHT; DWORD dwXOffset = m_pBricks[dwBrickIndex].dwBrickIndex * GAMEPARAM_BRICKWIDTH; pBackBuffer->FillRect(CRect(dwLeft+2, dwTop+2, dwRight+2, dwBottom+2), RGB(0, 0, 0), GDFILLRECT_OPACITY, &fillfx); pBackBuffer->BltFast(dwLeft, dwTop, m_pBrickSurface, CRect(dwXOffset, 0, dwXOffset+GAMEPARAM_BRICKWIDTH, GAMEPARAM_BRICKHEIGHT), 0, NULL); } } } if (bLevelClear) { GameInitBricks(pBackBuffer); if (m_dwGameMode == GAMEMODE_INGAME) { m_dwPlayerScore += 100; } } return S_OK; } HRESULT CMyApplication::GameInitBricks(CGapiSurface* pBackBuffer) { for (DWORD dwBrickIndex=0; dwBrickIndex < GAMEPARAM_NUMLINES * m_dwGameBricksPerLine; dwBrickIndex++) { m_pBricks[dwBrickIndex].bShowBrick = TRUE; m_pBricks[dwBrickIndex].dwBrickIndex = (DWORD) (3.0f * (float) rand() / (float) RAND_MAX); m_pBricks[dwBrickIndex].dwShineIndex = 0; m_pBricks[dwBrickIndex].dwExplosionIndex = 0; m_pBricks[dwBrickIndex].nExplosionCenterX = -1; m_pBricks[dwBrickIndex].nExplosionCenterY = -1; } return S_OK; } HRESULT CMyApplication::OnDisplayChanged(CGapiSurface* pBackBuffer) { ... DWORD dwGameBricksPerLine = (nScreenWidth - 2 * GAMEPARAM_BORDERWIDTH) / (GAMEPARAM_BRICKWIDTH + GAMEPARAM_BRICKSPACE); if (dwGameBricksPerLine < m_dwGameBricksPerLine) { // Screen width has been decreased, we need to redraw the bricks m_dwGameBricksPerLine = dwGameBricksPerLine; if (m_pBricks) { delete m_pBricks; } m_pBricks = new BREAKBRICK[m_dwGameBricksPerLine * GAMEPARAM_NUMLINES]; GameInitBricks(pBackBuffer); if (m_dwGameMode == GAMEMODE_INGAME) { // Reset the player score and ball index as well if we are in-game m_dwPlayerScore = 0; m_dwGameBallsLeft = 3; } } ... } HRESULT CMyApplication::GameInit(CGapiSurface* pBackBuffer, DWORD dwFlags) { ... if (m_pBricks) { delete m_pBricks; } m_dwGameBricksPerLine = (nScreenWidth - 2 * GAMEPARAM_BORDERWIDTH) / (GAMEPARAM_BRICKWIDTH + GAMEPARAM_BRICKSPACE); m_pBricks = new BREAKBRICK[m_dwGameBricksPerLine * GAMEPARAM_NUMLINES]; GameInitBricks(pBackBuffer); ... } Phew, that's quite some code just to add some bricks! Well, now on to more interesting things - explosions!
|