|
Documentation
|
Creating a complete game: BreakoutThis document has been updated for use with GapiDraw 4.0 or later. |
Step 6 : Bat actionOk, up until now our game has not been so exciting, just a scrolling background and some border graphics. Time to make it more interesting! We will now add a bat to the game, and also make the bat move automatically so we have a nice demo application!
First, download the file bat.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_BAT. Add the following code to myapplication.h: //----------------------------------------------------------------------------- // Name: class CBreakBat // Desc: Everything related to the bat //----------------------------------------------------------------------------- class CBreakBat { // Attributes protected: float m_nX; float m_nY; float m_nXSpeed; CGapiSurface* m_pBatSurface; // Construction public: CBreakBat(CGapiDraw* pGlobal, float nX=0.0, float nY=0.0) { m_nX = nX; m_nY = nY; m_nXSpeed = 0; m_pBatSurface = new CGapiSurface(pGlobal); } // Operations public: void ResetBat() { m_nX = 0; m_nY = 0; m_nXSpeed = 0; } int GetScreenX() { return (int) m_nX; }; int GetScreenY() { return (int) m_nY; }; float GetX() { return m_nX; } float GetY() { return m_nY; } float GetXSpeed() { return m_nXSpeed; } DWORD GetWidth() { return m_pBatSurface->GetWidth(); } DWORD GetHeight() { return (m_pBatSurface->GetHeight() / GAMEPARAM_NUMBATS); } CGapiSurface* GetSurface() { return m_pBatSurface; } void SetScreenX(int nX) { m_nX = (float) nX; } void SetScreenY(int nY) { m_nY = (float) nY; } void SetX(float nX) { m_nX = nX; } void SetY(float nY) { m_nY = nY; } void SetXSpeed(float nSpeed) { m_nXSpeed = nSpeed; } void MoveBatX() { m_nX += m_nXSpeed; } float BallBounce(CBreakBall* pBall) { float nDistance = GetX() + (float)(GetWidth() >> 1) - (float)(pBall->GetX() + (pBall->GetWidth() >> 1)); nDistance = - nDistance / (float) (GetWidth() >> 1); return (float) nDistance * 8.0f; } HRESULT DrawBat(CGapiSurface* pSurface) { int nBatX = GetScreenX(); int nBatY = GetScreenY(); return pSurface->BltFast(nBatX, nBatY, GetSurface(), CRect(0, 0, GetWidth(), GetHeight()), GDBLTFAST_KEYSRC, NULL); } HRESULT DrawBatShadow(CGapiSurface* pSurface) { int nBatX = GetScreenX() + 2; int nBatY = GetScreenY() + 2; GDBLTFASTFX bltfx; bltfx.dwFillColor = RGB(0,0,0); bltfx.dwOpacity = 64; return pSurface->BltFast(nBatX, nBatY, GetSurface(), CRect(0, 0, GetWidth(), GetHeight()), GDBLTFAST_KEYSRC | GDBLTFAST_COLORFILL | GDBLTFAST_OPACITY, &bltfx); } HRESULT CreateSurface(HINSTANCE hInstance) { HRESULT hr; if (SUCCEEDED(hr = m_pBatSurface->CreateSurface(0, hInstance, IDB_BAT, _T("PNG")))) { m_pBatSurface->SetColorKey(RGB(255, 0, 255)); } return hr; } // Implementation public: virtual ~CBreakBat() { delete m_pBatSurface; }; }; class CMyApplication : public CGapiApplication { ... CBreakBat* m_pBat; ... } Add the following code to myapplication.cpp: CMyApplication::CMyApplication(const GDAPPCONFIG& config) : CGapiApplication(config) { ... m_pBat = new CBreakBat(GetGlobal()); ... } CMyApplication::~CMyApplication() { ... delete m_pBat; ... } HRESULT CMyApplication::CreateVidMemSurfaces(CGapiSurface* pBackBuffer, HINSTANCE hInstance) { ... m_pBat->CreateSurface(hInstance); ... } HRESULT CMyApplication::OnDisplayChanged(CGapiSurface* pBackBuffer) { ... int nBatWidth = m_pBat->GetWidth(); m_pBat->ResetBat(); m_pBat->SetScreenX(nScreenXCenter - (nBatWidth>>1)); m_pBat->SetScreenY(nScreenHeight - 2*GAMEPARAM_BORDERWIDTH - GAMEPARAM_SCOREHEIGHT); ... } HRESULT CMyApplication::GameInit(CGapiSurface* pBackBuffer, DWORD dwFlags) { ... int nBatWidth = m_pBat->GetWidth(); m_pBat->ResetBat(); m_pBat->SetScreenX(nScreenXCenter - (nBatWidth>>1)); m_pBat->SetScreenY(nScreenHeight - 2*GAMEPARAM_BORDERWIDTH - GAMEPARAM_SCOREHEIGHT); ... } HRESULT CMyApplication::GameMoveBall(CGapiSurface* pBackBuffer) { int nScreenWidth = pBackBuffer->GetWidth(); int nScreenHeight = pBackBuffer->GetHeight(); int nScreenXCenter = nScreenWidth >> 1; int nScreenYCenter = nScreenHeight >> 1; m_pBall->MoveBall(); // Bounce ball on top of display if (m_pBall->GetScreenY() < GAMEPARAM_BORDERWIDTH) { m_pBall->SetYSpeed(-m_pBall->GetYSpeed()); m_pBall->SetScreenY(GAMEPARAM_BORDERWIDTH); } // Loose life if ball misses the bat if (m_pBall->GetScreenY() >= (int) (nScreenHeight-m_pBall->GetHeight() - GAMEPARAM_SCOREHEIGHT)) { if (m_dwGameMode == GAMEMODE_INGAME) { m_dwGameBallsLeft--; if (m_dwGameBallsLeft == 0) { m_dwGameMode = GAMEMODE_INTRO; } } // Reset ball position m_pBall->SetScreenX(m_pBat->GetScreenX() + (m_pBat->GetWidth() >> 1) - (m_pBall->GetWidth() >> 1) - 8); m_pBall->SetScreenY(GAMEPARAM_BRICKOFFSETY + GAMEPARAM_NUMLINES * (GAMEPARAM_BRICKHEIGHT + GAMEPARAM_BRICKSPACE)); m_pBall->SetYSpeed(m_nGameBallSpeed); m_pBall->SetXSpeed(0); } if (m_pBall->GetScreenX() >= (int) (nScreenWidth - GAMEPARAM_BORDERWIDTH - m_pBall->GetWidth())) { m_pBall->InvertXSpeed(); m_pBall->SetScreenX(nScreenWidth - GAMEPARAM_BORDERWIDTH - m_pBall -> GetWidth()); } if (m_pBall->GetScreenX() < GAMEPARAM_BORDERWIDTH) { m_pBall->InvertXSpeed(); m_pBall->SetScreenX(GAMEPARAM_BORDERWIDTH); } return S_OK; } HRESULT CMyApplication::GameCheckBat(CGapiSurface* pBackBuffer) { int nScreenWidth = pBackBuffer->GetWidth(); int nScreenHeight = pBackBuffer->GetHeight(); int nScreenXCenter = nScreenWidth >> 1; int nScreenYCenter = nScreenHeight >> 1; int nBatX = m_pBat->GetScreenX(); int nBatWidth = m_pBat->GetWidth(); int nBatHeight = m_pBat->GetHeight(); int nBallX = m_pBall->GetScreenX(); int nBallY = m_pBall->GetScreenY(); int nBallWidth = m_pBall->GetWidth(); int nBallHeight = m_pBall->GetHeight(); // Check for bat to border collision if (nBatX < GAMEPARAM_BORDERWIDTH) { m_pBat->SetScreenX(GAMEPARAM_BORDERWIDTH); } else if (nBatX >= (nScreenWidth - GAMEPARAM_BORDERWIDTH - nBatWidth)) { m_pBat->SetScreenX(nScreenWidth - GAMEPARAM_BORDERWIDTH - nBatWidth); } // Check for bat to ball collision if (nBallY >= (nScreenHeight - GAMEPARAM_SCOREHEIGHT - 2*GAMEPARAM_BORDERWIDTH - nBallWidth)) { if (nBallY < (nScreenHeight - GAMEPARAM_SCOREHEIGHT - 2*GAMEPARAM_BORDERWIDTH)) { if (((nBallX + nBallWidth) >= nBatX) && (nBallX <= (nBatX + nBatWidth))) { m_pBall->SetScreenY(nScreenHeight - GAMEPARAM_SCOREHEIGHT - nBallHeight - 2*GAMEPARAM_BORDERWIDTH); m_pBall->InvertYSpeed(); m_pBall->SetXSpeed(m_pBat->BallBounce(m_pBall)); } } } return S_OK; } HRESULT CMyApplication::GameMoveBat(CGapiSurface* pBackBuffer) { int nBallX = m_pBall->GetScreenX(); int nBatX = m_pBat->GetScreenX(); int nBatWidth = m_pBat->GetWidth(); if (m_dwGameMode == GAMEMODE_INGAME) { m_pBat->MoveBatX(); return S_OK; } // Make it cheat and move a bit faster on the intro screen so it never fails if (nBallX < (nBatX)) { m_pBat->SetXSpeed((float)(nBallX - nBatX)); m_pBat->MoveBatX(); } else if (nBallX > (nBatX + nBatWidth)) { m_pBat->SetXSpeed(nBallX - nBatWidth - nBatX + 1.0f); m_pBat->MoveBatX(); } /* // Uncomment this code and comment the above section if you // want the bat to move automatically according to the player rules if (nBallX < (nBatX + m_nGameBallSpeed)) { m_pBat->SetXSpeed(-m_nGameBallSpeed); m_pBat->MoveBatX(); } else if (nBallX > (nBatX + nBatWidth - m_nGameBallSpeed)) { m_pBat->SetXSpeed(m_nGameBallSpeed); m_pBat->MoveBatX(); } */ return S_OK; } HRESULT CMyApplication::GameDrawBackgrund(CGapiSurface* pBackBuffer) { ... // Draw ball shadow and bat shadow behind other objects m_pBall->DrawBallShadow(pBackBuffer); m_pBat->DrawBatShadow(pBackBuffer); ... } HRESULT CMyApplication::GameDrawObjects(CGapiSurface* pBackBuffer) { int nScreenWidth = pBackBuffer->GetWidth(); int nScreenHeight = pBackBuffer->GetHeight(); // Draw ball and bat m_pBall->DrawBall(pBackBuffer); m_pBat->DrawBat(pBackBuffer); return S_OK; } You should now have a moving ball and moving bat on your screen, and the ball should properly reflect on the bat and the walls! Now let's add some bricks!
|